An observation by @jaror made me wonder: why shouldn’t I make my monads “value strict”? I don’t know if I can formally define “value strict” but it means something like “when m a
has finished evaluating, a
is in WHNF[1]”. (That depends on a definition of “finished evaluating”, and I don’t know that there is one in general.) Anyway, here are some examples demonstrating what I mean.
- For a “value strict” monad,
return a
is the same asreturn $! a
- A “value strict”
State s a
would be isomorphic to\s -> ValueStrictPair a s
, wheredata ValueStrictPair a s = MkValueStrictPair !a s
- A “value strict”
Either a
would bedata RightStrictEither e a = Left e | Right !a
(These observations raise questions regarding whether the state itself in State
and the error type in RightStrictEither
should also be strict!) “Value strict” monads would have two benefits
- fewer space leaks due to accumulating thunks in values passed around in monadic computations.
- better code generation in general for those monadic computations (see, for example, State monad - memory exhausted - #13 by jaror)
There are two drawbacks
- They violate the monad law
return a >>= f == f a
, becausereturn undefined >>= const (pure ()) == undefined
. Maybe that’s OK. I don’t know that violating this law only up to strictness is a big deal. - They will do unnecessary work when you really did mean to be lazy. However, I suspect the vast, vast majority of use cases don’t really want to be lazy (like the vast majority of use cases of
foldl
really want to befoldl'
). It’s simple enough to recover lazy behaviour throughdata Lazy a = Lazy a
(at a very small, although non-zero cost) for those cases.
Have I missed some other drawback? Have I downplayed the importance of one of the two drawbacks above? If I’m defining my own monad, why shouldn’t I make it “value strict”?
[1] weak head normal form