Musings on Functional Programming and Haskell

Filed under: Software and Technology by Hari
Posted at 11:25 IST (last updated: Sun, Sep 8, 2013 @ 11:50 IST)
In this series < Previous

Caveat: I am not a computer programmer by profession. I am a hobbyist who loves to dig around programming in my free time. Many of the words used in this essay may not necessarily be formally correct and I invite knowledgeable people to share their feedback.

Since I've been fascinated by Haskell for a while now, I thought this would be a good time to post my general thoughts on Functional programming with respect to Haskell in particular.

To be honest, like most others coming from an imperative (procedural) programming background, functional programming was daunting. There were so many new concepts and some mathematical jargon used in Haskell tutorials that initially put me off, but then I kept digging deeper. I don't claim to understand most of the advanced concepts yet, but I have got a feel for functional programming that I think could act as a basis for future learning.

Haskell is a wonderfully abstract, pure functional programming language with features such as non-strict evaluation and a strong powerful static typing system and a rich set of useful libraries. To understand functional programming, here are some ideas I feel will help newbies (like me). There are a lot of tutorials that introduce FP in terms of purity, referential transparency etc. I assume that you have read those. Here I am going to try to condense my thoughts on how best FP can be understood without getting bogged down in theory.

Without further ado then...

It's all about data and data transformations

I like to visualize functional programming (FP) as primarily about data and how data is transformed. Think of (pure) functional programming as a series of data transformations that takes inputs and produces output with a guarantee that a certain input will always produce the same output. Data is passed in and out of functions and they assume different shapes (types), finally yielding the desired output.

It's amazing how quickly the necessity for imperative style disappears when seen along those terms. No longer do you require mutable or global variables since functions do the job of data mutation transparently. No longer do you require iterative looping, since functions can naturally be called recursively. Every other basic concept of FP fell into place when I grasped this.

No wonder then, that FP is a good fit for straightforward data-transformation problem domains. It is easy to learn functional programming by first approaching the obvious and direct applications of FP such as text/binary parsing and mathematical/numerical problems.

Other problem domains build on top of these bases, but introduce other conceptual distractions in terms of I/O, state management and user interaction.

Abstraction is not what it seems

Many from a programming background consciously or unconsciously assume that "abstraction" equals "implementation hiding". This is the common (but flawed) OOP usage. In functional programming, abstraction is seen as a way to derive a general pattern or solution by observing specific but distinct patterns or instances. The higher the level of "generalization" the tougher it is to mentally map the idea.

I think this is one reason why Haskell is tough to grasp. Sure, a lot of implementation hiding takes place in functional programming languages, but it's not just implementation hiding. A lot of concepts are abstract and tough to mentally grasp because abstractions describe generic patterns.

Books are good but write your own code

I've found that writing my own code is the best way to understand Haskell. Books and tutorials are great, but following them can be tough, because the difficulty of grasping abstract concepts can grow exponentially unless an earlier less abstract concept is understood thoroughly. Understanding can be greatly aided by application. And that is one reason why I think writing code without following along any tutorial/book is a good way to learn the language.

Not being bogged down by the theoritical portions of FP or unfamiliar problem domains is another reason to write code without any aid. The problem with tutorials or books is that they tend to introduce different problem domains to illustrate different concepts/techniques and sometimes the reader is not familiar enough with the problem domain in the first place. This severely hinders understanding of the concept of technique that is sought to be explained or illustrated.

The result of writing one's own code may be idiomatically incorrect, conceptually flawed, or straight out wrong, but unless one writes code, one cannot figure that out. The other thing I realized about writing one's own code is that one develops a kind of intuition for how things "work" in functional programming.

Pick a style and stick with it until concepts sink in

Some of the Haskell code I've seen on (even beginner) tutorials are extremely terse, yet quite tough to mentally scan and understand partly due to the strange syntax and partly due to the use of non-obvious higher order functions.

I find that being a bit more verbose initially is a good aid to conceptual understanding. 

I do understand that the style of programming does make a difference algorithmically, and sometimes in terms of efficiency or performance. However, once the concept is digested, it becomes much easier to scan terse code. What is important initially is to know the solution, and only then to figure out how to improve it. Being spoon fed with a perfect solution right at the start can be tough to conceptually digest.

It all takes time

All said and done, FP, especially as viewed through the lens of Haskell, is tough. It takes time and effort to learn even the basics, but can be fun too, because you are re-introduced to the basics and find newer avenues to explore classic problems. It's like rediscovering computer programming all over again.

I think #haskell on Freenode is a great way to connect with the knowledgeable members of the Haskell community. It can be daunting to read the some of the advanced topics discussed there, but newbies are always welcome and no question is too basic to answer.

9 comment(s)

Leave a comment »
  1. It's amazing how quickly the necessity for imperative style disappears when seen along those terms. No longer do you require mutable or global variables since functions do the job of data mutation transparently.


    Functions in almost all cases will not mutate the input, rather they will transform the input to a new output.

    Comment by Sidharth (visitor) on Mon, Sep 9, 2013 @ 21:19 IST #
  2. Functions in almost all cases will not mutate the input, rather they will transform the input to a new output.


    That is what I meant, in a more general sense. Functions are mutators of data, whether or not a new copy of output data is created or the existing input data in memory is modified (as may be the case sometimes in imperative programming). In a sense, it is an implementation detail that matters at a lower level of programming. Note the use of the term "Transparently." In functional paradigm, we leave it to the compiler to make certain decisions for us that are explicitly mentioned in lower level imperative languages (for examples, parametric references, pointers and values). The absence of these in a high level abstract FP language like Haskell is telling, since there are no "variables" (viewed as container of values in a memory location) in the first place.

    Data mutation happens everywhere in programming whether it is destructive (irreversible) mutation or not. The technical details vary across paradigms and situations.

    The confusing thing about FP tutorials is that they say exactly the same thing as you say and that makes it harder to grasp as to how one can live without destructive updates/mutable variables. We simply don't operate at that level of abstraction. My point is, issues like these are of a lower level of abstraction than what pure Functional Programming presents.

    Comment by Hari (blog owner) on Tue, Sep 10, 2013 @ 08:40 IST #
  3. The confusing thing about FP tutorials is that they say exactly the same thing as you say and that makes it harder to grasp as to how one can live without destructive updates/mutable variables. We simply don't operate at that level of abstraction.


    Here is an example of non-mutation updates:

    Suppose you have a list

    somelist = a->b->c->d->e->f->g

    where "->" is a pointer from one element to another). Now if you want to change c to z, then a new list will be created (by a piece of code / function in haskell

    somelist = a->b->c->d->e->f->g
    
    ^
    |
    newlist = a->b->x--|


    Note that 3 new elements were created i.e. a, b and x. But the older somelist and newlist share a part of old list (so this is not totally inefficient). So this is how we live without destructive updates.

    Most algorithms in functional programming will run slowly as a result of this (because imperative programming would have simply mutated the c the x in-place). However, there are some advantages by not using mutation. When you have multicore processors, it becomes very easy to parallelize purely functional programming languages. It also is often easier to reason about programs when state is not involved.

    Pick up any book or paper on functional data structures to understand the above in more detail.

    Comment by Sidharth (visitor) on Tue, Sep 10, 2013 @ 11:59 IST #
  4. somelist = a->b->c->d->e->f->g 
    
    ^
    |
    newlist = a->b->x--|


    Your fixed width is not working properly so the diagram above is not appearing correctly.

    Trying again.

    Comment by Sidharth (visitor) on Tue, Sep 10, 2013 @ 12:01 IST #
  5. Your fixed width still doesn't work.

    Trying editing the comment to see what I intended. You may want to move to better blog software -- wordpress / drupal / hosted offerings.

    Comment by Sidharth (visitor) on Tue, Sep 10, 2013 @ 12:03 IST #
  6. You may want to move to better blog software -- wordpress / drupal / hosted offerings.


    This is a self-coded blog. I'll take a look at my code and see the problem with BBCode parsing.

    Edit: Fixed.

    Comment by Hari (blog owner) on Tue, Sep 10, 2013 @ 13:49 IST #

  7. >somelist = a->b->c->d->e->f->g
    > ^
    > |
    >newlist = a->b->x--|


    Trying again. Adding > so that leading blank characters are not ignored.

    Comment by Sidharth (visitor) on Tue, Sep 10, 2013 @ 19:56 IST #
  8. Still not working. Diagram wrong. Blank characters should not be chomped. You should add a preview comment feature also.

    Frustrating :-( !

    Comment by Sidharth (visitor) on Tue, Sep 10, 2013 @ 19:58 IST #
  9. Still not working. Diagram wrong. Blank characters should not be chomped. You should add a preview comment feature also.

    Fixed. :-)

    Comment by Hari (blog owner) on Tue, Sep 10, 2013 @ 20:18 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