Online Book Reader

Home Category

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

By Root 548 0
the previous Pole to the landLeft landRight functions. If we examined the variables to which we bound our Maybe values, start would be (0, 0), first would be (2, 0) and so on.

Because do expressions are written line by line, they may look like imperative code to some people. But they’re just sequential, as each value in each line relies on the result of the previous ones, along with their contexts (in this case, whether they succeeded or failed).

Again, let’s take a look at what this piece of code would look like if we hadn’t used the monadic aspects of Maybe:

routine :: Maybe Pole

routine =

case Just (0, 0) of

Nothing -> Nothing

Just start -> case landLeft 2 start of

Nothing -> Nothing

Just first -> case landRight 2 first of

Nothing -> Nothing

Just second -> landLeft 1 second

See how in the case of success, the tuple inside Just (0, 0) becomes start, the result of landLeft 2 start becomes first, and so on?

If we want to throw Pierre a banana peel in do notation, we can do the following:

routine :: Maybe Pole

routine = do

start <- return (0, 0)

first <- landLeft 2 start

Nothing

second <- landRight 2 first

landLeft 1 second

When we write a line in do notation without binding the monadic value with <-, it’s just like putting >> after the monadic value whose result we want to ignore. We sequence the monadic value but we ignore its result, because we don’t care what it is. Plus, it’s prettier than writing its equivalent form of _ <- Nothing.

When to use do notation and when to explicitly use >>= is up to you. I think this example lends itself to explicitly writing >>=, because each step relies specifically on the result of the previous one. With do notation, we need to specifically write on which pole the birds are landing, but every time we just use the pole that was the result of the previous landing. But still, it gave us some insight into do notation.

Pattern Matching and Failure


In do notation, when we bind monadic values to names, we can utilize pattern matching, just as in let expressions and function parameters. Here’s an example of pattern matching in a do expression:

justH :: Maybe Char

justH = do

(x:xs) <- Just "hello"

return x

We use pattern matching to get the first character of the string "hello", and then we present it as the result. So justH evaluates to Just 'h'.

What if this pattern matching were to fail? When matching on a pattern in a function fails, the next pattern is matched. If the matching falls through all the patterns for a given function, an error is thrown, and the program crashes. On the other hand, failed pattern matching in let expressions results in an error being produced immediately, because the mechanism of falling through patterns isn’t present in let expressions.

When pattern matching fails in a do expression, the fail function (part of the Monad type class) enables it to result in a failure in the context of the current monad, instead of making the program crash. Here’s its default implementation:

fail :: (Monad m) => String -> m a

fail msg = error msg

So, by default, it does make the program crash. But monads that incorporate a context of possible failure (like Maybe) usually implement it on their own. For Maybe, it’s implemented like so:

fail _ = Nothing

It ignores the error message and makes a Nothing. So when pattern matching fails in a Maybe value that’s written in do notation, the whole value results in a Nothing. This is preferable to having your program crash. Here’s a do expression with a pattern match that’s bound to fail:

wopwop :: Maybe Char

wopwop = do

(x:xs) <- Just ""

return x

The pattern matching fails, so the effect is the same as if the whole line with the pattern were replaced with a Nothing. Let’s try this out:

ghci> wopwop

Nothing

The failed pattern matching has caused a failure within the context of our monad instead of causing a program-wide failure, which is pretty neat.

The List Monad

So far, you’ve seen how Maybe values can be viewed as values with a failure context, and how we can incorporate failure handling

Return Main Page Previous Page Next Page

®Online Book Reader