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++").
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.
I'm not sure this is an accurate statement. I've been recently trying to teach myself functional programming, and I've found that a sort "bottom-up" style of programming really helps me. To use your example of merge sort:
:)
I could be misusing the term, but this feels like bottom-up design to me. Functional programming is different than object-oriented programming, but you shouldn't need to totally abandon existing design techniques when switched between the two.
It’s import to get used to thinking that data can be used as code, and vice versa.
Usually you construct a program (data) using several primitive operations (folding, nesting, threading, distributing, ..., and some are generalized inner product, outer product, etc.), and use this program (data) to manipulate other data.
I agree.
My advice for this: read The Little Schemer. You can follow it in Erlang. It's a good book to drill a sense of this into you.
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:
Trace of my thought process:
inputNumber
. scanf() written.primeFlag
declared and set equal to1
.primeNumber
against every number from 2 toprimeNumber/2
.for
loop started. Declared a loop variablei
to checkprimeNumber
against.primeNumber
against eachi
. The moment we find even onei
that dividesprimeNumber
, setprimeFlag
to0
andbreak
. Loop body written.for
loop, check the value ofprimeFlag
and report it to the user. printf() written.In Haskell:
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
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.