Assuming
testValidateFEN :: Spec
testBuildBoard :: Spec
and the module Test.hspec
is imported.
I am reading the following line of code:
hspec (testValidateFEN >> testBuildBoard)
My questions are:
- Does
Spec
have an instance of theMonad
typeclass ? - Isn't that line of code equivalent to
hspec testBuildBoard
since the first action result is discarded ?
CodePudding user response:
- Yes, Spec does have a Monad instance.
- The result of the first action is discarded, but its effect is still carried out.
Could you explain how would you de-sugar the expression
hspec (testValidateFEN >> testBuildBoard)
If you look at the definition of Spec
, you'll see that it's a synonym for SpecWith
, which in turn is a synonym for SpecM
, which in turn is a monad stack of Writer IO wrapped in a newtype, with the Monad
instance derived from the underlying stack.
So when you combine two of those with a >> b
, that desugars into a >>= \_ -> b
according to the definition of >>
, which means "first run a
, then take its output, throw it away, and then run b
" - according to the description in the Monad
class.
As you combine testValidateFEN :: Spec
and testBuildBoard :: Spec
via >>
, you get a new value, also of type Spec
, which, when executed, would first execute testValidateFEN
, take its return value, throw it away, and then execute testBuildBoard
.
Now, the crucial thing to realize here is that the important "output" of these Spec
actions is not in their return values, but in the list of SpecTree a
values (see definition of SpecM
) that they accumulate via the Writer
monad as a side-effect of their execution. Therefore, if you execute testValidateFEN
, it will accumulate some SpecTree a
values via the Writer
monad, and then if you execute testBuildBoard
, it will accumulate some more SpecTree a
values, and the total resulting list of [SpecTree a]
would be the combined lists that testValidateFEN
and testBuildBoard
would have produced on their own.