Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing Monadic Code

Tags:

haskell

monads

I'm learning Haskell by working through the Haskell course by Brent Yorgey. I just reached the monad section, and while I think I (finally) have a decent grasp on how to work with monads, I don't understand how to test code that uses them.

The exercise for this section is to write a (simplified) Risk simulation, and it requires heavy use of the Rand StdGen monad. In particular, we have to write the following function:

type Army = Int

data Battlefield = Battlefield { attackers :: Army, defenders :: Army }

battle :: Battlefield -> Rand StdGen Battlefield

It takes an initial battlefield, and runs a simulation of how this battle would go.

I have an implementation for it, but I don't understand how to test it. I can't "get at" the values inside the Rand StdGen Battlefield returned by battle, so I can't print them out at the GHCI interpreter, which is how I've been testing my code so far. I also can't figure out how to print the result of a battle in a Haskell main function, or something. How do people go about testing these sorts of functions?

like image 765
anjruu Avatar asked Sep 21 '14 14:09

anjruu


1 Answers

You can "get at" the result of a random computation using functions like evalRand and friends. evalRand takes a 'starting' RandomGen and runs the monadic computation deterministically.


Here's my hand-waving, non-rigorous explanation of what evalRand is for:

One of the differences between monads and imperative programming is that a monad is a representation of a computation, not the computation itself. In other words, when Haskell evaluates an expression like a >>= b >>= c (or the equivalent do notation), it's just putting the Lego bricks together, so to speak - the computation doesn't take place until you execute the monad using a function like evalRand.

For a simpler example, think about composing functions together. . gives you a function which represents the computation carried out by the two functions it composes. You only get a return value from that computation when you actually call the function with an argument.

That's why many of the monads in the standard library (with the exception of IO, which is executed by the run-time system) provide a "trap door" function like evalRand. It's how you actually use the computation you just defined in your monadic code.

like image 146
Benjamin Hodgson Avatar answered Nov 06 '22 12:11

Benjamin Hodgson