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.