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)
In functional programming, a monad is a software design pattern with a structure that combines program fragments (functions) and wraps their return values in a type with additional computation.
A monad is an algebraic structure in category theory, and in Haskell it is used to describe computations as sequences of steps, and to handle side effects such as state and IO. Monads are abstract, and they have many useful concrete instances. Monads provide a way to structure a program.
Common monadsNondeterminism using List monad to represent carrying multiple values. State using State monad. Read-only environment using Reader monad. I/O using IO monad.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With