Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

What are the challenges that an experienced programmer with the usual C++/UNIX background would face when trying to learn functional programming? The main motive for learning is to gain a fresh perspective, perhaps that'd help write better code. This is debatable though.

Should Haskell be the introductory language or Prolog or Lisp or F#?

share|improve this question

closed as too broad by gnat, Simon, GlenH7, Kilian Foth, Glenn Nelson Dec 19 '13 at 14:02

There are either too many possible answers, or good answers would be too long for this format. Please add details to narrow the answer set or to isolate an issue that can be answered in a few paragraphs.If this question can be reworded to fit the rules in the help center, please edit the question.

    
F# is great on Windows. –  Jon Harrop Dec 29 '10 at 19:02

6 Answers 6

up vote 9 down vote accepted

When I started digging deep into Haskell (coming from a mostly Basic/C background), I already had a firm grasp of closures from thinking about them for months and months, and using them in JavaScript. Whether or not this is the biggest hurdle of learning functional programming, I'm not sure, but I'd imagine they're easier to understand when state is taken out of the equation.

I suppose the first major hurdle for me was the syntax. When I first took a look at the xmonad source code, it looked quite humorous:

-- | Set focus explicitly to window 'w' if it is managed by us, or root.
-- This happens if X notices we've moved the mouse (and perhaps moved
-- the mouse to a new screen).
focus :: Window -> X ()
focus w = local (\c -> c { mouseFocused = True }) $ withWindowSet $ \s -> do
    let stag = W.tag . W.workspace
        curr = stag $ W.current s
    mnew <- maybe (return Nothing) (fmap (fmap stag) . uncurry pointScreen)
            =<< asks mousePosition
    root <- asks theRoot
    case () of
        _ | W.member w s && W.peek s /= Just w -> windows (W.focusWindow w)
          | Just new <- mnew, w == root && curr /= new
                                               -> windows (W.view new)
          | otherwise                          -> return ()

The arrows pointing here and there, and things like maybe (return Nothing), gave me the impression that they make it up as they go, and that the syntax has zillions of operators and keywords (custom operators were a foreign concept to me).

The next hurdle was Haskell's type system. The nice thing about this one is, if you don't quite get it, the compiler will be quick to point it out :-)

Then, monads. I suppose my breakthroughs in understanding them were:

  • do {x <- m; k x} is equivalent to m >>= k
  • Monads are merely a design pattern. They are not given special treatment by the language (other than do notation), and one does not have to understand category theory to use them.
  • Monads, functors, etc. are all, in an abstract sense, "actions" yielding a "thing".
    • With a Functor, you can apply a function to that thing, but you don't get to unwrap it (e.g. (*5) <$> Just 2 = Just 10).
    • An applicative functor lets you take two actions and apply one's result to the other's, yielding a new action (e.g. Just (*5) <*> Just 2 = Just 10). This introduces a notion of sequence, but does not allow the result of one action to influence what the next will be.
    • Monad, despite its simple definition (bind and return), can do everything an applicative functor can do and more. Namely, monads allow the result of one action to determine the next (e.g. getLine >>= \x -> if null x then return () else print (read x * 2)).

As for an introductory language for functional programming, I can certainly vouch for Haskell. Thanks to learning Haskell, I feel like I've gotten better at breaking up programs logically, even in languages like C and assembly.

PS: I didn't say anything about the transition from imperative to functional, and I probably should (to really answer the question), but I need to go to sleep.

share|improve this answer
    
Thanks Joey. Any recommended books? –  Fanatic23 Dec 22 '10 at 16:47
1  
@Fanatic23: I learned Haskell mostly from web resources, IRC, and spending way too much time using it. However, I've seen Learn You a Haskell for Great Good! and Real World Haskell mentioned again and again as good books for learning Haskell, and they're both available on the web for free. –  Joey Adams Dec 22 '10 at 22:43
    
I can vouch for "Real World Haskell" as well. It's a pretty good introduction to the language. –  davidk01 Dec 22 '10 at 23:56
    
I much preferred Learn you a Haskell. –  Jon Harrop Dec 29 '10 at 19:01
1  
I would suggest first studying Haskell to get familiar with functional concepts. Then move on to something like Scala or Erlang for writing real software :) –  James Adam Dec 18 '13 at 15:02

I guess wrapping your head around Monads and all the other categorical nonsense is the biggest challenge.

share|improve this answer
2  
This would have been a good answer without the words "categorical nonsense." –  Larry Coleman Dec 22 '10 at 13:44
8  
@Larry Coleman: "categorical nonsense" is actually a technical term used by mathematicians. It's not nonsense in the same way as regular nonsense. –  davidk01 Dec 22 '10 at 23:51

Functional programming is mainly a style, just like OO programming. Some languages are created with this style in mind (Haskell, ML, Scheme, etc. for functional; Java, C#, C++, etc. for OO), but the principles can be applied to most languages.

I'd say start with a language your already comfortable with, preferably the "most functional" (eg. if you know C and Python, go with Python since it has first class functions). As you learn about functional programming principles (no mutable state, no side-effects, etc.) try following them in your language of choice.

The best advice I can give is to think in terms of values rather than statements. Imperative programming is all about "which action should I perform next?", whereas functional programming is all about "what do I want this to be?". This manifests in a lot of places, some of which I've mentioned below. The difference can be summed up by the following:

// An imperative mindset performs a series of actions

// Check the value of "foo"
if (foo) {
  // Set the value of "bar"
  bar = FALSE;
}
else {
  // Set the value of "bar"
  bar = TRUE;
}

// A functional mindset asks what value "bar" should have

// "bar" is the opposite of "foo"
bar = !foo;

Here are a few ways to become more "value-oriented":

  • Static Single Assignment: define new variables instead of mutating existing ones. Mutating variables is a symptom of imperative thinking ("then do this"), whereas defining a load of constant values is a symptom of value thinking ("I'll need this as well").
  • Pretend you're using lazy evaluation: if your code is defining a value, it will work under any evaluation strategy (assuming it halts). If your code is highly imperative, it will break under lazy evaluation (since it's effectively 'executed backwards').
  • Separate functions from procedures: try to make each function you write either calculate a value (ie. a pure function) OR perform some side-effect (ie. a procedure) but not both. This way all of your calculations can focus on their result value, rather than worrying about interleaving of effects.
  • Factor out common function-chaining patterns like maps, folds, unfolds, zips, etc. If your language has first-class functions (or hygenic macros) then you'll learn to appreciate them. If not, you'll learn to want them!
  • Use ternary expressions: in imperative languages, if/then/else is used to send execution down different paths, which is a very non-functional thing to do. Instead of choosing different actions to perform, try using ternary operators to choose different values to assign. I'd recommend using extra variables rather than nesting them though, since they can get difficult to understand (just like the branching logic of an imperative program).
  • Use lookup tables instead of switch statements: if your language supports first-class functions, why not try this

(In PHP):

// Imperative
switch ($foo) {
  case 0:
    zero();
  case 1:
    one();
    break;
  default:
    def();
}

// Functional
$table = NULL;
$table = [
  function() use (&$table) {
    zero(); call_user_func($table[1]);
  },
  'one'];
call_user_func(
  array_key_exists($foo, $table)? $table[$foo]
                                : 'def');
  • Use recursion instead of loops: be careful, as you might overflow your stack! Then you'll appreciate tail-call optimisation. Many languages have built-in "map", "reduce" (AKA "fold"), etc. which don't have this problem. If not, you can define these yourself using loops then never have to write another loop again.
  • Group related values together: for example, an if statement which sets different values for "x", "y" and "z" could be turned into a ternary statement which chooses between dictionaries containing keys "x", "y" and "z".
  • Use pattern-matching to break down values, for example:

(In PHP):

// Imperative
if ($foo) {
  $x = 'hello';
  $y = 'world';
  $z = 'wibble';
}
else {
  $x = 'some';
  $y = 'different';
  $z = 'strings';
}

// Functional
list($x, $y, $z) = $foo? ['hello', 'world', 'wibble']
                       : ['some', 'different', 'strings'];
  • Use comprehensions: these are inherently functional, since they're a mixture of maps and filters, and are a quick way to get into a functional way of thinking. For example:

(In Python):

results = [foo(x) for x in bar if baz(x)]
  • Use values to represent effects: instead of calling procedures deep within your application, you can instead make a note of the procedure you want to call and put those notes into your return value. For example, instead of calling "foo" with "x", "y" and "z" you could return a list "[("foo", x), ("foo", y), ("foo", z)]}" and get the caller to run them for you. Applying this same principle, the caller can instead put them into its return value and pass them up to its caller, and so on. This way, lots of your procedures become pure functions: they're calculating a list of procedures to call, rather than calling procedures directly. This may sound silly at first, but it's basically an inverse of dependency-injection. It's a bit more boilerplate, but it offers some nice advantages: behavioural tests don't need mock objects (just look at the procedure-list), more code is unit-testable since it doesn't cause effects, procedures can be ignored/rearranged/decorated/retried, etc. Note that this is a form of message queue, and also an instance of the "command pattern".
  • Factor out pattern in your procedure-lists: "I don't care about the results, just run them in this sequence" is exactly what functional programmers call a Functor; "Chain these together so the result of the nth procedure is sent into the (n+1)th procedure" is exactly what functional programmers call an Applicative Functor; "The nth procedure could be this or that, depending on what the (n-1)th precedure returns" is exactly what functional programmers call a Monad.

In my opinion, these are good programming practices anyway. Once you're used to them, you'll find that languages desgined for functional programming (eg. Haskell) make these practices much easier (eg. they have tail-call optimisation, they make mutable variables explicit, they have very powerful pattern matching and comprehensions, etc.)

share|improve this answer

The best experience I had as a UNIX developer learning lisp was learning how to write emacs extensions using emacs lisp (elisp). The ability to have your text editor do anything you want is a powerful learning incentive, and you don't have to write much lisp code to get emacs to do something useful.

share|improve this answer

When you learn a different paradigm you learn a different way to approach the problem. The challenge there is retraining yourself to not immediately go down the path of a procedural or OO solution. For the most part I think most of your challenges will be of that nature. The concepts and applications are pretty straightforward, getting used to it was the hard part for me.

I'm a big fan of scheme which is similar to lisp.

share|improve this answer

Haskell seems awesome, but I have never really been able to learn it - there are a lot of hurdles to get over before you can really do anything (the type system is ultra-strict).

I learned all of my functional programming using lisp, and other dynamic languages (plus C++), so you certainly don't need to learn haskell to learn new things.

That said, I think there is a parallel with the phenomenon that programmers who learn iteration first usually find iteration hard to understand, and vice versa.

Accordingly, my practical advice to you is to start with haskell, and if you find you can learn what you need to there, explore other functional programming languages.

Good luck!

share|improve this answer

Not the answer you're looking for? Browse other questions tagged or ask your own question.