Monads for Control Flow - Sequence, Selection and Iteration
clojure, control-flow, haskell, monads, scala
Solution
For sequencing in Haskell, you have the functions `>>=` and `sequence`:
(>>=) :: Monad m => m a -> (a -> m b) -> m b
sequence :: Monad m => [m a] -> m [a]
The `>>=` or bind function takes a monadic action, extracts the value from it and feeds it into a function that returns a new monadic action. The `sequence` function takes a list of monadic actions of the same type and executes all of them, aggregating their results and wrapping it as a single action.
For iteration you have `mapM` and `forM` (`forM = flip mapM`)
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
The `mapM` and `forM` functions are for applying a function that returns an action to each element in a list, aggregating the results as a single action.
For selection, I assume you mean conditionals, which are implemented in Haskell as just if-the-else expressions. They can be used directly in monadic expressions the same as they can be used in pure expressions. However, you can also use certain monads for performing choices or at least handling errors. The easiest to grok is the `Maybe` monad:
data Maybe a = Nothing | Just a
instance Monad Maybe where
return a = Just a
(Just a) >>= f = f a
Nothing >>= f = Nothing
It has a very simple implementation. Essentially, if you try to sequence a `Nothing` into anything else, it will return `Nothing` every time. This gives you the notion of short-circuited failure:
lookup :: Eq a => a -> [(a, b)] -> Maybe b
-- Looks up a value in a key-value association list
myFunc :: Int -> [(String, Int)] -> Maybe Int
myFunc mult assocList = do
i <- lookup "foo" assocList
j <- lookup "bar" assocList
return $ i * mult + j
Here, if the lookup for `"foo"` fails, the `myFunc` immediately returns `Nothing`. Similarly if the lookup for `"bar"` fails, `myFunc` immediately returns `Nothing`. It's only when both lookups succeed does `myFunc` do any computation. This provides a sort of "error handling". There's a similar monad `Either a`
data Either a b = Left a | Right b
instance Monad (Either a) where
return a = Right a
(Right a) >>= f = f a
(Left a) >>= f = Left a
that works very much the same, except the "failure" value can carry some context, such as a string error message or the state of the computation at the point of failure.
Problem
I can see how you'd use a Monad is Haskell for IO - to create a container around the computation for this operation. It makes sense that you can use Monads to 'plug computations together' - as you would compose functions for a dataflow operation. What I'm only just grokking is that you can use Monads for Control Flow. Now I understand Control flow to be about Sequence, Selection and Iteration. Now I'm comfortable with higher order functions like map, foldl, filter and zipwith/mapcat to perform operations on lists. My question is - can I do sequence, selection and iteration with monads in order to achieve control flow? (Happy for an answer in Haskell, Scala or Clojure)