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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With