Sebastian Fischer wrote: > Heinrich Apfelmus wrote: >> >> The reason is that you have chosen the "wrong" type for your >> continuation monad; it should be >> >> newtype CMaybe a = CMaybe (forall r. (a -> Maybe r) -> Maybe r) > > Yes, with this type `orElse` has the same type as `mplus`, which is very > nice. > > <Aside> > >> Personally, I recommend to stop thinking about continuations altogether >> and instead use the approach I've outlined in "The Operational Monad >> Tutorial" > > I appreciate your operational monad tutorial both for the idea and how > you explained it. But the advice "stop thinking about X because Y is > better" feels odd to me. Before I know by myself that Y is better than X > (which requires thinking about both X and Y) I don't feel comfortable > following such advice. Afterwards, I don't need such advice ;)
Very true. :) My flimsy "personally" was an attempt to declare my recommendation optional. I failed to say the right thing even then, for I don't mean to stop thinking about continuations in general, just to discourage them as foundation for implementing other monads. > There may be more to X than just Y. IIRC, there is more to > 'continuations' than 'monads'. For example, the implementation of > `callCC` does not type check with your changed data type. Ah, indeed, callCC in the operational setting is much trickier than I thought. However, it also seems to be the reason why your original approach does not work so well! Basically, your choice of implementation newtype CMaybe r a = CMaybe ((a -> Maybe r) -> Maybe r) supplies a default semantics for callCC . But this means that when implementing orElse , you also have to consider its interaction with callCC , even when you actually don't want to expose or implement a callCC function. As for the interaction: what should ((callCC ($ 0) >> mzero) `orElse` return 2) >>= return . (+3) be? If the scope of callCC should not extend past orElse , then this evaluates to return 5 . But this choice of scope dictates the type that Holger mentioned. If the the scope of callCC should extend beyond the orElse , so that the whole thing evaluates to mzero , orElse will have the type of mplus . But then, I think that your implementation type CMaybe needs to be more sophisticated because orElse now needs to detect whether the argument contains a call to callCC or not in order to distinguish ((callCC ($ 0) >> mzero) `orElse` return 2) >>= return . (+3) ==> mzero from (mzero `orElse` return 2) >>= return . (+3) ==> return 5 In short, the interaction between orElse and callCC is tricky, and it would be unfortunate to be forced to consider it due to a premature choice of implementation type. This can't happen with the operational approach, because that one merely implements the "free" monad over a set of operations. > I shall try to implement a monad that supports two choice operations > (one which fulfills the distributive law and one which satisfies the > cancellation property) with the operational package. The main task will probably be to figure out the interaction between mplus and orElse , i.e. to consider what stuff like a `orElse` (b `mplus` c) should evaluate to. Regards, Heinrich Apfelmus -- http://apfelmus.nfshost.com _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe