Open rymndhng opened 8 years ago
Yeah, I've actually run into this but haven't fixed it (obviously). A kind of stupid workaround for now is to just insert a do-nothing return expression at the beginning. Thanks for the report though, will work on a fix.
I'll just drop some thoughts in here ...
Actually, on further thought, there is a real question about how to treat this. The above example translates thus:
(>>= (throw (RuntimeException. "AHA!"))
(fn [failure] (>>= (right :something)
(fn [_] :success))))
And if you think of mdo
as just sugar for a series of nested binds, then this is quite correct (and the reason for the problem becomes apparent, since >>=
is a regular function). Basically, we always have eager evaluation, it's just that the evaluation of the later expressions doesn't take place until their post-macroexpansion enclosing functions are called. (Poor man's laziness, whereas Haskell has real laziness from the get-go so even the initial expression isn't evaluated until demanded.)
Of course it's possible to have mdo
insert some extra fanciness (or just to insert the not-fancy workaround mentioned above, at the cost of traversing an extra bind at the beginning), but that make the relationship between mdo
and >>=
& friends less obvious. (>>=
and >>
etc. could also just be macros that delay
their first argument, but that seems like it would involve a lot of overhead.)
It's still a PITA of course.
Agree with your concerns. Thinking about it as syntactic
sugar makes sense to "not" be the libraries problem.
My background isn't in haskell, but my mental model for mdo
is that whatever is inside the mdo
is expected to be lazy so that I can pass the computation around and potentially evaluate it in different contexts, i.e try/catch
and transaction
.
Again, I'm not sure if there's a good solution for this -- though at minimal maybe this could be added to the README.
I'm seeing that the first expression inside an
mdo
is always evaluated, which may execute side-effects before the monad is run.For example:
This throws the runtime exception before I try to execute it with
monads.core/run-monad