Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

"functional programming languages haven’t taken off in the industry as a whole"

You mean, except for Erlang?



If Erlang is an example of "taken off"...

I am currently using Erlang on a project because it was a good choice, but I would not exactly consider it popular. Popular is stuff like Javascript, Python, Ruby - even Go.

I think it's indicative that in an example of how simple Haskell is supposed to be, you find this several paragraphs in:

> This is an example of a monad transformer, namely the composition of StateT ( State Transformer ) with the IO Monad. The values from the IO monad are lifted into the State monad with the liftM function.

That makes me curious - I'd like to figure out what all that means, exactly, what the concepts are, what they're useful for, and so on.

But I don't know if I'm a "typical web developer" either.


Monad transformers is one of the trickiest parts of using monads, and certainly they're not well explained at the example - I'm afraid the linked article does an awful job at introducing monads to web developers, when most of the basic terms used remain without a proper definition.

For what I've learned about transformers (although I haven't used those at practical projects), they are used to combine several monads and wrap them around a single value with several layers of monadic types, so that the adequate monad can handle the inner value when the occasion arises. But don't take my word for that; understanding transformers "does" require knowing a good deal of category theory.


Using monad transformers doesn't require any category theory, just Haskell and an understanding of Haskell's use of monads (which are inspired by category theory but aren't going to help you much with proofs). It might help in writing them - I've not written any that weren't toy examples, so I couldn't say.

The idea behind monad transformers is simply that they wrap a monad and give back a monad (which could in principle be wrapped again).

So you can have, for instance,

    StateT Int (ReaderT String IO)
You would have a monad that carries a modifiable integer, an unmodifiable string, and which sequences IO actions.

There is a function called lift which wraps a monadic value in a transformer. So for the above, for the unfamiliar, ReaderT provides

    ask :: ReaderT a m a
which here would be specialized to

    ask :: ReaderT String IO String

If I were working in that monad in do notation, I could do something like:

    str <- ask
and get back the string the reader provides in str. If I were working in our entire stack, though, ask isn't the same type - so I could instead do something like:

    str <- lift ask
to turn a ReaderT String IO String into a StateT Int (ReaderT String IO) String.

So if I had:

    flip runReaderT "foo" $ flip runStateT 7 $ do
        str <- lift ask
        modify (\ x -> x + length str)
        i <- get
        lift $ lift $ putStrLn $ str ++ show i
we could look at the type of the entire do notation expression. It must have a StateT Int on the outside, because the first thing we do with it is runStateT; it must then have a ReaderT String because the next thing is unwrapping a ReaderT. Running through it:

        str <- lift ask
grabs a String ("foo") out of an ask that's been lifted into our stack from a ReaderT String IO String

        modify (\ x -> x + length str)
At the top level, updates the state in our StateT with the result of applying the function to the current value. In this case, 7 + 3 = 10, so the StateT on the outside now holds 10.

        i <- get
grabs that 10 from the StateT and calls it i

        lift $ lift $ putStrLn $ str ++ show i
Appends the string we grabbed ("foo") and the stringification of i ("10"); this is passed to putStrLn which gives us a value of type IO () that, when evaluated, will print "foo10". IO is not the monad we're working in, though, so we lift it into ReaderT and then lift the ReaderT into StateT, to be ultimately unwrapped. The whole expression here has type IO (), and could serve as a definition for main if you want to play around with it.

I should admit that I lied a little bit. The lifts are actually not necessary, because of some typeclass voodoo that I think makes it slightly harder to understand coming in but usually easier to use (unless you need to nest two transformers of the same type...). What's done is that get and ask and such aren't defined just for the one type but for anything that implements MonadState or MonadReader (respectively, as it happens). Then, there is an instance defined for "any monad transformer wrapping anything that implements MonadState", &c, which obviously propagates up the stack, so most of the time you can leave out explicit calls to lift.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: