tags:

views:

372

answers:

5

I like reading snippets of code about concepts that I don't understand. Are there any snippets that show off monads in all their glory? More importantly how can I apply monads to make my job easier.

I use jQuery heavily. That's one cool application of monads I know of.

+1  A: 

Your question is really vague -- it's like asking, "show an example of code that uses variables". It's so intrinsic to programming that any code is going to be an example. So, I'll just give you the most-recently-visited Haskell function that's still open in my editor, and explain why I used monadic control flow.

It's a code snippet from my xmonad config file. It is part of the implementation for a layout that behaves in a certain way when there is one window to manage, and in another way for more than one window. This function takes a message and generates a new layout. If we decide that there is no change to be made, however, we return Nothing:

handleMessage' :: AlmostFull a -> SomeMessage -> Int -> Maybe (AlmostFull a)
handleMessage' l@(AlmostFull ratio delta t) m winCount =
  case winCount of
    -- keep existing Tall layout, maybe update ratio
    0 -> finalize (maybeUpdateRatio $ fromMessage m) (Just t)
    1 -> finalize (maybeUpdateRatio $ fromMessage m) (Just t)

    -- keep existing ratio, maybe update Tall layout
    _ -> finalize (Just ratio) (pureMessage t m)
  where
    finalize :: Maybe Rational -> Maybe (Tall a) -> Maybe (AlmostFull a)
    finalize ratio t = ratio >>= \ratio -> t >>= \t ->
      return $ AlmostFull ratio delta t

    maybeUpdateRatio :: Message -> Maybe Rational
    maybeUpdateRatio (Just Shrink) = Just (max 0 $ ratio-delta)
    maybeUpdateRatio (Just Expand) = Just (min 1 $ ratio+delta)
    maybeUpdateRatio _             = Nothing

We decide what to return based on the current window manager state (which is determined by a computation in the X monad, whose result we pass to this function to keep the actual logic pure) -- if there are 0 or 1 windows, we pass the message to the AlmostFull layout and let it decide what to do. That's the f function. It returns Just the new ratio if the message changes the ratio, otherwise it returns Nothing. The other half is similar; it passes the message onto Tall's handler if there are 2 or more windows. That returns Just a new Tall layout if that's what the user asked for, otherwise it returns Nothing.

The finalize function is the interesting part; it extracts both ratio (the desired new ratio) and t (the desired new Tall layout) from its Maybe wrapper. This means that both have to be not Nothing, otherwise we automatically return Nothing from our function.

The reason we used the Maybe monad here was so that we could write a function contingent on all results being available, without having to write any code to handle the cases where a Nothing appeared.

jrockway
Perhaps we could take the example a step further and write it as, `finalize ratio t = AlmostFull <$> ratio <*> pure delta <*> t`.
Anthony
Of course, then it's not a monad example anymore.
jrockway
Hah, okay so in the name of being literal then, `return AlmostFull \`ap\` ratio \`ap\` return delta \`ap\` t`
Anthony
@Anthony: But it's still not a monad example; you're using only the applicative subset of the monad's power
pelotom
@pelotom: That was my point. The example jrockway provided is a very nice example of the flexibility of composition in Haskell, but isn't an example of what Monads specifically offer.
Anthony
+1  A: 

I've been looking into Haskell and Information Flow security. This paper is pretty interesting, it uses Monads to enforce confidentiality in Haskell Programs.

http://www.cse.chalmers.se/~russo/seclib.htm

mayahustle
+3  A: 

Like others, I think the question is far too general. I think most answers (like mine) will give examples of something neat making use of one specific monad. The real power of monads is that, once you understand them as an abstraction, you can apply that knowledge to any new monads you come across (and in Haskell there are a lot). This in turn means you can easily figure out what new code does and how to use it because you already know the interface and some rules that govern its behavior.

Anyway, here's an example using the List monad from a test-running script I wrote:

runAll :: IO ()
runAll = do
  curdir <- getCurrentDirectory
  sequence $ runTest <$> srcSets <*> optExeFlags <*> optLibFlags
  setCurrentDirectory curdir

Technically I'm using the Applicative interface, but you can just change the <*>'s to ap from Control.Monad if that bothers you.

The cool thing about this is that it calls runTest for every combination of arguments from the lists "srcSets", "optExeFlags", and "optLibFlags" in order to generate profiling data for each of those sets. I think this is much nicer than what I would have done in C (3 nested loops).

John
+1  A: 

Essentially, monads are "imperative minilanguages". Hence, they enable you to use any imperative construct like exceptions (Maybe), logging (Writer), Input/Output (IO), State (State), non-determinism (lists [a]), parsers (Parsec, ReadP) or combinations thereof.

For more advanced examples, have a look at the example code for my operational package. In particular,

  • WebSessionState.lhs implements web sessions that are programmed as if the server were a persistent process while they are in fact delivered asynchronously.
  • TicTacToe.hs shows a game engine where players and AI are written as if they were running in concurrent processes.
Heinrich Apfelmus
+1 for operational
John
Sadly the link to the page about the operational package seems to be broken.
Tsuyoshi Ito
@Tsuyoshi Ito - it's broken for me now too, although I'm pretty sure it worked yesterday. Hopefully an editor will fix it soon.
John
Oops, there was a superfluous `)` in the link. Fixed.
Heinrich Apfelmus
`Maybe` is not like an exception. It's most equivalent to `null` or `nil` in conventional OO languages — it expresses the idea of "I have no value to give you". `Either` is more similar to an exception. I also don't see how `Maybe` is in any way imperative, and ditto for the List monad.
Chuck
Yes, `Maybe` can express exceptions. The difference between `Maybe` and `Either` is that the latter includes information about the error, whereas the former only gives "succeeded with `a`" or "failure". Concerning the question whether `Maybe` and lists are imperative: they are implementations (in parentheses) for two mini-languages with imperative semantics (before parentheses). So, they are not imperative per se, but can be used as such. Examples: `lookup key map >>= lookup key2` and `sequence [[1,2],[3,4]]`.
Heinrich Apfelmus
A: 

Here is something that I did recently that might show off some of the power of monads. The actual code is not shown here to protect the innocent, however the idea is the same.

Let's say you want to search through some dictionary and depending on what you find you want to do some other search. The searches might return Nothing (the element you are looking for doesn't exist) in wich case you might try a different search, and if all searches fail you return Nothing.

This is a perfect oppertunity for using ReaderT Dictionary Maybe . Here is one where we search through are dictionary for the keys 1 2 3 4 5, and return the value stored in either 1 and either 2 or 3 or 4 and 5. That is either 1 or 4, but if your going to go with 1 you should also find either 2 or 3, and if you try 4 you should also find 5.(the mplus can be read as or in this case.)

import Control.Monad
import Control.Monad.Reader

find a = ReaderT (lookup a)
both a b = liftM2 (++) a b

search = both (find 1) ((find 2) `mplus` (find 3)) 
         `mplus` both (find 4) (find 5)

And running:

(runReaderT search) [(1,"a"),(3,"c"),(4,"d"),(5,"g")] --> Just "ac"

(runReaderT search) [(6,"a")] --> Nothing

Of course this is just a small example of what this abstraction is able to do.

HaskellElephant