Reputation: 6210
I had always figured that Haskell's do
-notation was just sugar for monad composition (>>=
).
Then I came across this instance of it, and am not sure what monad is in use here, or whether there even is one:
rollDieThreeTimes :: (Die, Die, Die)
rollDieThreeTimes = do
let s = mkStdGen 0
(d1, s1) = randomR (1, 6) s
(d2, s2) = randomR (1, 6) s1
(d3, _) = randomR (1, 6) s2
(intToDie d1, intToDie d2, intToDie d3)
intToDie :: Int -> Die
...
My questions:
do
-notation of this example?do
-notation that permits this
format of let
-statements with no monad?do
-notation into >>=
form?Upvotes: 5
Views: 640
Reputation: 907
One important thing to know about do-notation is that its rules are applied before type checking. Those rules are (roughly):
do { x; y }
==> x >> do { y }
(with rules applied recursively)do {x <- a; b }
==> a >>= \x -> do { b }
do {{pat} <- a; b }
==>
a >>= \x -> case x of
{pat} -> do { b }
_ -> fail "Pattern match fail"`
do { let {decls}; b }
==> let {decls} in do { b }
do { a }
==> a
those last two rules are the important ones here - any expression on its own which has no (implicit) semicolons will just become itself. So as long as you don't make use of the first two rules, you can have expressions of any type in your code, without the need for any monad. Following those rules, your code goes from
rollDieThreeTimes :: (Die, Die, Die)
rollDieThreeTimes = do
let s = mkStdGen 0
(d1, s1) = randomR (1, 6) s
(d2, s2) = randomR (1, 6) s1
(d3, _) = randomR (1, 6) s2
(intToDie d1, intToDie d2, intToDie d3)
to
rollDieThreeTimes :: (Die, Die, Die)
rollDieThreeTimes =
let s = mkStdGen 0
(d1, s1) = randomR (1, 6) s
(d2, s2) = randomR (1, 6) s1
(d3, _) = randomR (1, 6) s2
in (intToDie d1, intToDie d2, intToDie d3)
and is then type checked.
Upvotes: 2
Reputation: 92067
do
should always be used in a monadic context. But if you only have one expression in the do
-block, then it desugars to just that expression, so there are no references to >>=
, and no Monad constraint arises.
So you can write
do let x = a
y = b
foo
which is equivalent to
let x = a
y = b
in foo
but really, you should just write the latter.
I haven't read HPFFP, which Google tells me is probably the book you got this example from. It comes from the chapter on State, and it seems like the author(s) introduce this as a simple state-less example, then later add state to it. Perhaps they chose this unusual construction for pedagogical reasons, for example to keep it similar to a later version using monadic binds in place of the let bindings.
Upvotes: 5