: this is not a post bashing either functional programming or Haskell, but from a newbie's perspective, why learning a functional programming can be painful especially for those programmers who have a strong foundation in OOP and procedural programming. Rather my intention is to encourage learning the FP paradigm and warn about potential pitfalls. Again, being a newbie myself, I appreciate that this might not be a true representation of what functional programming is all about, but that is not the purpose of this article. My thanks to the folks at #haskell on FreeNode and others for their thoughts and insights. As a result I'll be making small corrections/additions to this article as I go on.
Following up on earlier article on my learning Haskell
, I have to say that learning a functional programming language is unlike anything I've learned before. In many ways, functional programming is like learning to program all over again and it's not something like OOP which just extends the concepts of procedural programming into new directions. Without further ado, then...
The learning curve is not linear
This is to be expected. Diving straight into functional programming, I found that the basic concepts aren't that hard at all and this can lull one into a false sense of security. Things like statelessness, immutability of "variables", lack of side effects, and pure functions are theoritically quite easy to grasp. But it's one thing to be familiar with a concept, but totally another to model and condition your mind to map a problem into the functional approach.
One fundamental early pitfall could be recursion. The most common problem that is introduced when teaching recursion is the factorial problem. It's actually quite simple and ridiculously intuitive when presented as a solution. Here is how a factorial is defined in functional style (pseudo-code):
factorial (0) = 1
factorial (n) = n * factorial (n-1)
What it means is that you define a factorial of a number N
as the number N
multipled by the factorial of N-1
. So the function calls itself till it calls factorial (0). This is called a "base case" (where recursion is terminated). This is fairly easy to understand. However, it is far from intuitive from the point of view of working out a solution for a problem that one might solve using an iterative approach.
A real world problem may illustrate this better: counting pebbles. The instinctive way to counting a number of pebbles that are laid out in a row is simply to point a finger at each pebble, one at a time, and mentally increment a counter for every pebble you point at. This is an iterative approach. Another less obvious (but still easy) method is simply to pick up a pebble and keep it in your palm and repeat the function until you run out of pebbles. The number of pebbles in your palm at the end of the process is the answer. You've just "folded" the problem using an accumulator (your palm) instead of using your finger (counters). Of course, there will always be a debate as to which is more efficient but that is besides the issue but I think it's a good way to mentally "map" recursion and iteration as any.
Functional programming favours the second approach and FP languages support recursion by optimization techniques, even implicit recursion through folding - a concept that keeps recurring in the FP paradigm.
Recursion is only the tip of the iceberg though. But it is certainly an early barrier that needs crossing.
Too much theory can be off-putting
One thing I found when searching for tutorials/articles on Functional Programming is theory. You'll run into a lot of theory and they can easily confuse you and lead to a conclusion that "functional programming is for academicians". While it's inevitable that you need some grounding in mathematics for functional programming, it's not strictly a requirement. But even then, the terminology can be heavy, especially with relation to the more advanced concepts like Functors, Monoids, Monads etc. I confess that I still don't know them and trying to even familiarize myself with them is frustrating. I would suggest that letting go of these topics initially and focussing on the basics as more important.
Tutorials that make it seem simpler
Actually for Haskell, the excellent guide "Learn You A Haskell for Great Good
" makes Haskell seem ridiculously easy at first look. But actually the learning difficulty begins when some of the more advanced concepts are introduced. Pitfall one for most people I expect will be "higher order functions." It doesn't become 'slightly harder' after that. It becomes ten times harder unless you are already conditioned to functional programming intuitively. Actually I would recommend reading a few chapters early on and try to really grasp the basics first. Going too fast, like with imperative programming languages, will lead to confusion.
Everything "looks" intuitive in functional programming when presented as a solution (ignoring the more advanced concepts and syntactical complexities involved, for a moment). Arriving to that solution is precisely where you find the roadblocks.
My recommendation would be probably to swallow the concepts in small bits and write working code on your own for simple programs using those concepts. It might be slow going, but then again, learning is never a piece of cake.
Forget the Zealotry or just ignore it
This might seem controversial, but a lot of articles I read on the internet sound a lot like zealotry to me, extolling the virtues of Functional Programming and making it appear that imperative programming are for the mentally inferior ape-types. Sure, that is putting it a bit high, but it can be a bit depressing to read such articles. Whether FP paradigm is truly superior or not is besides the issue. Point is, to a newbie, it can seem daunting and downright depressing to see your second nature (read: imperative programming methodology) trashed like that. Frequently you come across "code samples" solving the same problem: one with the imperative approach with exaggerated clumsiness and the other (almost unreadble, but elegant looking) functional solution ("isn't that clever!" kind of solutions)
Ignore that (except for the code bit which can be educational once you're more comfortable with FP). Such talk/writing adds nothing of value to a newbie and to my mind it creates too many expectations about what FP can offer. While terms like "expressive", "rich", "elegant" and "graceful" are thrown around frequently in reference to FP, to a newbie it is just marketing talk in my opinion. Maybe it can deliver, maybe it cannot. But as a newbie, such articles are a distraction from learning and can even lead to demotivation (why don't I just *get* it?)
The above are just some of the few pitfalls that a programmer new to FP paradigm (with a focus on Haskell) is likely to fall into. I might add, there are a few other points to add to the above, but they are relatively minor and can be overcome with familiarity and practice.
- Type system: This is specific to Haskell, but nonetheless, the strong, static type system can be an irritation and annoyance to those used to dynamically typed languages. However, with practice, it gets easier to understand and one actually begins to appreciate its strengths.
- Syntax and syntactic sugar: I never heard of the latter term until I came across Haskell and I confess that these elements can be a bit confusing at first. Every new programming language (including imperative languages) has this issue, so it's not unqiue to FP paradigm (though it can be a slightly harder hurdle in FP).
- Associativity: This is another common term used in FP paradigm and learning the associativity of functions can be a little confusing as can concepts like compositing, currying etc. Simply learning to "read how this code works" can be challenging at first and takes time to begin to understand.
- Laziness and lazy evaluation: This is specific to Haskell and while the concept itself sounds very intuitive, it can be a bit confusing to understand "how" it works in practice especially if you're still thinking like an imperative programmer. I would suggest forgetting about understanding it and just take it for granted until you run into a situation where it actually matters whether a function works lazily or not.
These are just some of my observations from a very "newbie" level. Unfortunately I've not found too many articles or essays documenting things from a total newbie's perspective and while the LYAH guide comes close to "understanding" the newbie, it is still written by a person who is well experienced and knowledgeable in Haskell and the guide sometimes reverts to "expert-speak" inadvertently. I welcome suggestions and feedback on this article from everybody!
Postscript: Reactions and Responses
There is some misunderstanding in reference to the bit about "recursion." My point wasn't that recursion is difficult or hard or unfamiliar to a programmer from an imperative background. My point was a recursive solution to every kind of repeating pattern is not always intuitive for those used to iterative thinking. And that could be a minor roadblock in understanding Functional Programming early on,.