
Henning Thielemann wrote:
Am 12.08.2014 um 21:17 schrieb Edward Kmett:
so it seems the logical extension of your request would be to strip all such instances,
one by one, please :-)
Can someone provide me examples of useful applications of mfix on Maybe, ExceptT etc.?
I tend to agree that those MonadFix instances are not immensely useful. As far as I can see, all one can do is a certain amount of knot-tying on the value level on the fly. Going beyond that is prevented because nothing is known about the return value of 'mfix f' until 'f' has run to a successful (non-error) completion; if the success depends in any way on the argument to 'f', the computation will bottom out. For example, a parser for a simple language with gotos could be based on parseProgram :: Labels -> StateT String Maybe (Program, Labels) where 'Labels' stores a map from label names to program locations. Then 'mfix' can be used to feed the resulting labels right back into 'parseProgram'. The use of 'mfix' is not essential though. In principle, one can transform parseProgram into a function parseProgram' :: StateT String Maybe (Labels -> (Program, Labels)) and use an ordinary fixpoint to tie the knot. But 'mfix' buys some convenience. (Note that this convenience comes at a fairly big cost in the case of the list monad's 'mfix' implementation, which reruns the whole list computation for each element of the result list.) I think that this is basically true for all uses of 'mfix' for monads that do not have a linear flow of computation.
I mean, all those mfix instances contain an "error" - wouldn't it be better if the user would do such dirty things and insert a more specific error message?
If those particular error messages turn up then they indicate programming errors: using 'mfix f' in such a way that success depends on the argument of f, or letting the argument of 'f' escape in the case of an unsuccessful evaluation. So I still think that those are the right instances, even though the error messages are not perfect.
Admittedly, I have used mfix only once so far, at all, namely on an RWS monad, as far as I remember. Eventually I chose another design, because the mfix way was too fragile.
I've also found 'mfix' to be of limited use in practice, but there are some cute applications, like spawning a thread, passing it its own thread id: mfix (\tid -> forkIO (f tid)). Cheers, Bertram