Online Book Reader

Home Category

Learn You a Haskell for Great Good! - Miran Lipovaca [150]

By Root 498 0
(Just 8):

joinedMaybes :: Maybe Int

joinedMaybes = do

m <- Just (Just 8)

m

Perhaps the most interesting thing about join is that for every monad, feeding a monadic value to a function with >>= is the same thing as just mapping that function over the value and then using join to flatten the resulting nested monadic value! In other words, m >>= f is always the same thing as join (fmap f m). It makes sense when you think about it.

With >>=, we’re always thinking about how to feed a monadic value to a function that takes a normal value but returns a monadic value. If we just map that function over the monadic value, we have a monadic value inside a monadic value. For instance, say we have Just 9 and the function \x -> Just (x+1). If we map this function over Just 9, we’re left with Just (Just 10).

The fact that m >>= f always equals join (fmap f m) is very useful if we’re making our own Monad instance for some type. This is because it’s often easier to figure out how we would flatten a nested monadic value than to figure out how to implement >>=.

Another interesting thing is that join cannot be implemented by just using the functions that functors and applicatives provide. This leads us to conclude that not only are monads as strong as functors and applicatives, but they are in fact stronger, because we can do more stuff with them than we can with just functors and applicatives.

filterM


The filter function is pretty much the bread of Haskell programming (map being the butter). It takes a predicate and a list to filter and then returns a new list where only the elements that satisfy the predicate are kept. Its type is this:

filter :: (a -> Bool) -> [a] -> [a]

The predicate takes an element of the list and returns a Bool value. Now, what if the Bool value that it returned was actually a monadic value? What if it came with a context? For instance, what if every True or False value that the predicate produced also had an accompanying monoid value, like ["Accepted the number 5"] or ["3 is too small"]? If that were the case, we would expect the resulting list to also come with a log of all the log values that were produced along the way. So, if the Bool that the predicate returned came with a context, we would expect the final resulting list to have some context attached as well. Otherwise, the context that each Bool came with would be lost.

The filterM function from Control.Monad does just what we want! Its type is this:

filterM :: (Monad m) => (a -> m Bool) -> [a] -> m [a]

The predicate returns a monadic value whose result is a Bool, but because it’s a monadic value, its context can be anything from a possible failure to nondeterminism and more! To ensure that the context is reflected in the final result, the result is also a monadic value.

Let’s take a list and keep only those values that are smaller than 4. To start, we’ll just use the regular filter function:

ghci> filter (\x -> x < 4) [9,1,5,2,10,3]

[1,2,3]

That’s pretty easy. Now, let’s make a predicate that, aside from presenting a True or False result, also provides a log of what it did. Of course, we’ll be using the Writer monad for this:

keepSmall :: Int -> Writer [String] Bool

keepSmall x

| x < 4 = do

tell ["Keeping " ++ show x]

return True

| otherwise = do

tell [show x ++ " is too large, throwing it away"]

return False

Instead of just returning a Bool, this function returns a Writer [String] Bool. It’s a monadic predicate. Sounds fancy, doesn’t it? If the number is smaller than 4, we report that we’re keeping it, and then return True.

Now, let’s give it to filterM along with a list. Because the predicate returns a Writer value, the resulting list will also be a Writer value.

ghci> fst $ runWriter $ filterM keepSmall [9,1,5,2,10,3]

[1,2,3]

Examining the result of the resulting Writer value, we see that everything is in order. Now, let’s print the log and see what we have:

ghci> mapM_ putStrLn $ snd $ runWriter $ filterM keepSmall [9,1,5,2,10,3]

9 is too large, throwing it away

Keeping 1

5 is too large, throwing it away

Keeping

Return Main Page Previous Page Next Page

®Online Book Reader