Why learning Functional Programming and Haskell in particular can be hard

Filed under: Tutorials and HOWTOs by Hari
Posted at 19:52 IST (last updated: Fri, Dec 30, 2011 @ 15:30 IST)
In this series < Previous Next >
Note: 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?)

And finally...

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,.

9 comment(s)

Leave a comment »
  1. Personally I find recursion and higher order functions trivial compared to Haskell's type system. In my experience you can't get very far with Haskell without understanding the type system. The Haskell type system goes far, far beyond the static typing you find in imperative languages like C++ or Java. An essential part of the type system is monads and monad stacks. In particular, monads are not "theory" but really at the core of the language.

    Comment by Kevin Jardine (visitor) on Mon, Oct 31, 2011 @ 22:52 IST #
  2. My personal route to understanding FP was to start and stop multiple times. The first time around I just barely understood recursion. The second time around I understood recursion, higher-order functions and combinators. I'm currently going through it for the third time and working my way up through functors to monads and the like. In between each time I go back to imperative/OO programming languages (generally Python) and try to apply the things I learn from FP to a more comfortable setting. As a side note, I learned most of my FP knowledge from SICP and Scheme, not Haskell. I think Scheme is a better starting point if you want to learn FP. Haskell has a number of features (pattern matching, laziness) that can get in the way of understanding the basic core of FP.

    Comment by Shrutarshi Basu (visitor) on Mon, Oct 31, 2011 @ 23:27 IST #
  3. I always enjoy articles like this. i am Haskell vet yet relate to everything you say. i wish we would spend more time trying to address thesr issues.

    Haskell folks do care about beginners and worl hard to try and flatten the learning curve from regular PLs. But some simpler things could be done too and i think your post provodes some great hints.

    Comment by Chris Dornan (visitor) on Mon, Oct 31, 2011 @ 23:55 IST #
  4. Your &quot;pseudo-code&quot; is in fact perfectly valid Haskell, though it would be more idiomatic to leave out the trivial parens in the pattern matches.

    Comment by fshcakes (visitor) on Tue, Nov 1, 2011 @ 01:59 IST #
  5. I agree, functional programming is not easy for newcomers to grasp. It's best to start with procedural programming and work your way up to functional programming. E.g., start by learning C and then read Real World Haskell, which uses many easy-to-understand C parallels.

    Real World Haskell

    http://book.realworldhaskell.org/read/

    Comment by Andrew Pennebaker (visitor) on Tue, Nov 1, 2011 @ 03:26 IST #
  6. This seems to be an honest impression of how you found Haskell and it seems reasonably fair considering that you are new. I remember going through most of the same thoughts that you did too when I started and I have watched many peers try to program in Haskell. If there is one thought I believe it is that Haskell is for mature developers; mature with respect to experience and skill. The more skilled you are in other languages and programming, the easier you will find Haskell, the more likely you are to enjoy it rather than being scared by it. I found that the students that have been programming for longer enjoy Haskell more. That you are even a novice in Haskell and are interested enough to really try and make it work I would take as an excellent sign. My advice would be to stick with it; you have to work harder and think harder in Haskell to write the same program, but that program will be more correct, easier to maintain , extend, read and make assurances about than the equivalent imperative program. Ultimately all mature developers still program in imperative languages, they will all use the most appropriate tool for the job, but Haskell is an invaluable part of your toolset. Doing things properly in Haskell even once changes the way you write and think about your imperative code. (Sorry for the little rant, I think I made some sort of point and thankyou for the honest article.)

    Comment by Robert Massaioli (visitor) on Tue, Nov 1, 2011 @ 04:03 IST #
  7. Kevin Jardine, thanks. The typing system is crucial to the Haskell experience, but strangely I don't find it too hard. Maybe I've not explored enough into its depths.

    Shrutarshi Basu, thanks for your views. Yes, I dived straight into Haskell because I figured why not take the dive? I am aware that most people recommend "softer" functional languages first.

    Chris Dornan, thanks for your comments. :-)

    fshcakes, thanks. That shows that Haskell closely matches the way mathematical functions are represented (in the computer, at least.) :)

    Andrew Pennebaker, thanks for your comments. I am already very comfortable in procedural languages. My point was that even with the knowledge, it can be tough.

    Robert Massaioli, thanks for your thoughts. Yes, I plan to stick to learning Haskell as I enjoy the learning process as much as the end result. :)

    Comment by Hari (blog owner) on Tue, Nov 1, 2011 @ 07:06 IST #
  8. I think your difficulties reflect the extra emphasis on abstraction and correctness that is dear to the FP community. Even introductory textbooks on FP (e.g. Bird & Wadler's 'Introduction to F.P.' or Hutton's 'Programming in Haskell' ) show how prove properties or derive programs from specifications. The equivalent imperative presentation would probably be mathematically harder (Hoare logic, pre- and post-conditions, loop invariants). AFAIK no current introductory textbopk to C/C++/C#/Java follows that path.

    Comment by Pedro Vasconcelos (visitor) on Thu, Nov 3, 2011 @ 22:17 IST #
  9. Pedro, thanks for the comment. Yes, the abstraction part definitely requires considerable mental adjustment. And the theory part does confuse programmers without a reasonably good mathematical background (particularly in category theory).

    Comment by Hari (blog owner) on Thu, Nov 3, 2011 @ 22:25 IST #

Leave a comment

First-time comments on this blog are moderated.
Your name*
Email ID*
(wont' be published)
Website
Your comments*
(No HTML allowed)
:-) :-D :biggrin: :-P ;-) 8-) :-( :mad: |-| :oops: :-/ :-| :roll:
bold italic quote code
Code* captcha Enter the code you see in the image
* required fields