Online Book Reader

Home Category

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

By Root 536 0
computations whose results are of different types, but the type of the state must stay the same. Now why is that? Well, for instance, for Maybe, >>= has this type:

(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b

It makes sense that the monad itself, Maybe, doesn’t change. It wouldn’t make sense to use >>= between two different monads. Well, for the State monad, the monad is actually State s, so if that s were different, we would be using >>= between two different monads.

Randomness and the State Monad


At the beginning of this section, we talked about how generating random numbers can sometimes be awkward. Every random function takes a generator and returns a random number along with a new generator, which must then be used instead of the old one if we want to generate another random number. The State monad makes dealing with this a lot easier.

The random function from System.Random has the following type:

random :: (RandomGen g, Random a) => g -> (a, g)

This means it takes a random generator and produces a random number along with a new generator. We can see that it’s a stateful computation, so we can wrap it in the State newtype constructor by using the state function, and then use it as a monadic value so that passing the state is handled for us:

import System.Random

import Control.Monad.State

randomSt :: (RandomGen g, Random a) => State g a

randomSt = state random

So, now if we want to throw three coins (True is tails, and False is heads), we just do the following:

import System.Random

import Control.Monad.State

threeCoins :: State StdGen (Bool, Bool, Bool)

threeCoins = do

a <- randomSt

b <- randomSt

c <- randomSt

return (a, b, c)

threeCoins is now a stateful computation, and after taking an initial random generator, it passes that generator to the first randomSt, which produces a number and a new generator, which is passed to the next one, and so on. We use return (a, b, c) to present (a, b, c) as the result without changing the most recent generator. Let’s give this a go:

ghci> runState threeCoins (mkStdGen 33)

((True,False,True),680029187 2103410263)

Now doing things that require some state to be saved in between steps just became much less of a hassle!

Error Error on the Wall

You know by now that Maybe is used to add a context of possible failure to values. A value can be a Just something or a Nothing. However useful it may be, when we have a Nothing, all we know is that there was some sort of failure—there’s no way to cram more information in there telling us what kind of failure it was.

The Either e a type also allows us to incorporate a context of possible failure into our values. It also lets us attach values to the failure, so they can describe what went wrong or provide other useful information regarding the failure. An Either e a value can either be a Right value, signifying the right answer and a success, or it can be a Left value, signifying failure. Here’s an example:

ghci> :t Right 4

Right 4 :: (Num t) => Either a t

ghci> :t Left "out of cheese error"

Left "out of cheese error" :: Either [Char] b

This is pretty much just an enhanced Maybe, so it makes sense for it to be a monad. It can also be viewed as a value with an added context of possible failure, but now there’s a value attached when there’s an error as well.

Its Monad instance is similar to that of Maybe, and it can be found in Control.Monad.Error:

instance (Error e) => Monad (Either e) where

return x = Right x

Right x >>= f = f x

Left err >>= f = Left err

fail msg = Left (strMsg msg)

return, as always, takes a value and puts it in a default minimal context. It wraps our value in the Right constructor because we’re using Right to represent a successful computation where a result is present. This is a lot like return for Maybe.

The >>= examines two possible cases: a Left and a Right. In the case of a Right, the function f is applied to the value inside it, similar to the case of a Just where the function is just applied to its contents. In the case of an error, the Left value is kept, along with

Return Main Page Previous Page Next Page

®Online Book Reader