As a newbie in functional languages (I started touching Erlang a couple of weeks ago -- the first functional language I could get my hands on).
I started to writing some small algorithms (such as left_rotate_list
, bubble_sort,
merge_sort
etc.). I found myself often getting lost in decisions such as "should I use a helper List for intermediate result storage?" and "should I create a helper function to do this?"
After a while, I found that functional programming (bear with me if what I am talking does not make sense at all) encourages a "top down" design: i.e., when I do merge_sort, you first write down all the merge sort steps, and name them as individual helper functions; and then you implement those helper functions one by one (and if you need to further dividing those helper functions, do it in the same approach).
This seems to contradict OO design a little, in which you can start from the bottom to build the basic data structure, and then assemble the data structure and algorithms into what you want.
Thanks for comments. Yes, I want to get advice about how to "think in functional language" (just like "thinking in Java", "thinking in C++").
It will make your productivity plummet Maybe you've even been wondering whether you should try it next. The short answer is hell no! Functional programming is full of flaws, is not suitable for real-world projects, and will make your productivity plummet.
An answer is that functional programming is to program using functions, as they are defined in mathematics (in short, side-effect free things that map values from the domain to the codomain). To actually translate that into "how to think" is the hand-waving part that is difficult to be exhaustive about, but I'll sample some of my thoughts:
Number one is associated with the vague: "elegant code". List comprehensions can present very succinct and mathematical-equations like definitions of functions. Just look at the quick-sort implemented with LCs. This is how I define elegance, succinct and makes all behaviours clear. Not that perl code-golf where you are most often terse and cryptic.
Number two is something that I use day to day in all programming. Divide code into functions (methods, routines, etc...) of current state that are side-effect free computations giving inputs to the next action to take (even which the next action to take is). When the value is returned, give it to a routine that performs the action that is described, then start over.
In my head I diagram an Erlang process as a state machine graph, where each vertex is a side-effect and a function whose output is which edge to chose out of the vertex. The high regard of side-effects is something the functional programming paradigm taught me. Especially in Erlang, since side-effects really matter in concurrency, and Erlang makes concurrency very available.
The same way some isolated tribes have only one word for numbers above 3, or no words for "mine"/"yours". It feels like popular languages do not have words for "this will cause a side-effect", but Functional Programming has it. It is forcing you to be aware of it all the time, and that is a good thing.
After a while, I found that functional programming [...] encourages a "top down" design.
Well, it's not about "top down" or "bottom up" design really. It's about focusing on the "what" of the problem at hand, rather than the "how". When I started off with functional programming, I found that I kept recalling imperative constructs like the nested for
loop in C. Then I quickly found out that trying to translate my imperative thinking to functional constructs was very difficult. I'll try to give you a more concrete example. I'll implement an equivalent program in C and Haskell and attempt to trace my thought process in both cases. Note that I've been explicitly verbose for the purpose of explanation.
In C:
#include <stdio.h>
int main(void)
{
int i, inputNumber, primeFlag = 1;
scanf("%d", &inputNumber);
for(i = 2; i <= inputNumber / 2; i ++)
{
if (inputNumber % i == 0)
{
primeFlag = 0;
break;
}
}
if (primeFlag == 0) printf("False\n");
else printf ("True\n");
return 0;
}
Trace of my thought process:
inputNumber
. scanf() written.primeFlag
declared and set equal to 1
.primeNumber
against every number from 2 to primeNumber/2
. for
loop started. Declared a loop variable i
to check primeNumber
against.primeNumber
against each i
. The moment we find even one i
that divides primeNumber
, set primeFlag
to 0
and break
. Loop body written.for
loop, check the value of primeFlag
and report it to the user. printf() written.In Haskell:
assertPrime :: (Integral a) => a -> Bool
assertPrime x = null divisors
where divisors = takeWhile (<= div x 2) [y | y <- [2..], mod x y == 0]
Trace of my thought process:
null divisors
.divisors
? First, let's write down a list of possible candidates. Wrote down Texas range from 2 to number/2.mod x y == 0
I want to get advice about how to "think in functional language"
Ok, first and foremost, think "what", not "how". This can take a lot of practice to get used to. Also, if you were formerly a C/C++ programmer like me, stop worrying about memory! Modern languages have a garbage collector, and it's written for you to use- so don't even try to modify variables in place. Another thing that has personally helped me: write down English-like definitions in your program to abstract out the functions that do the heavy-lifting.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With