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.