Some code kata – haskell while loops
In the previous post we solved the following problem using a for loop
Write three functions that compute the sum of the numbers in a given list using a for-loop, a while-loop, and recursion.
I have not found while loop primitives in the standard library but a library for this exists on hackage and it is called Control.Monad.Loops. It provides the following implementation
whileM_ :: (Monad m) => m Bool -> m a -> m ()
whileM_ p f = go
where go = do
x <- p
if x
then f >> go
else return ()
Looking at the type signature we see that it takes a bool wrapped in a monad as a first argument and another monad which is the action as a second argument. The result, as with the for loop, is a monad containing nothing (or more correct the unit value).
Again, since we want to do stateful programming we will turn to the State monad. This time we need to keep both the remaining list and the sum within the state. A tuple seems like a natural choice.
The most complicated part is the condition. We use the get function to retrieve the current state. The get function has the following type signature
get :: MonadState s m => m s
It simply returns the state as a value wrapped within the monad. However, we want to test wether the list is empty or not. fmap or the equivalent applicative functor <$> is a function that allow us to apply an ordinary function to the value wrapped within the monad – transforming it to something else which in this case should be a boolean value indicating whether we should continue looping or not.
This is the type signature for fmap and <$> respectivly
(<$>) :: Functor f => (a -> b) -> f a -> f b fmap :: Functor f => (a -> b) -> f a -> f b
To test whether the list is empty we can then do the following (assuming that the list is stored as the first element in the state tuple)
not . null . fst <$> get
The final result, when putting this together, may look like below
import Control.Monad
import Control.Monad.State
whileM_ p f = go
where go = do
x <- p
if x
then f >> go
else return ()
sumUsingWhile xs = snd $ execState loop (xs,0)
where
loop = whileM_ (not . null . fst <$> get) $
modify $ \(x:xs,sum) -> (xs, x+sum)