blame: mtl MonadReader instance for ContT

This mtl instance is in conflict with a usage of shift/reset that I'm porting from ML (Sheard's type-directed partial evaluator).
instance (MonadReader r' m) => MonadReader r' (ContT r m) where local f m = ContT $ \c -> do r <- ask local f (runContT m (local (const r) . c))
I need to remove the "local (const r)" bit. I think this is just a different point in the diverse design space of dynamic binding/delimited control. Since Haskell lacks visibility management for importing instances, I'm forced to duplicate the rest of the ContT code in order to replace this instance with my own. 1) I am hoping this list can recall the reason for putting this design decision into the mtl. Perhaps there's a reason to prefer this particular side-effect interaction. 2) I am suggesting that the mtl be restructured such that this instance be delegated to a separate module so that i) I can avoid importing it along with the other useful instances ii) and the mtl could provide alternative instances. An alternative to breaking out the modules would be to not provide the instance. Another, more radical alternative could involve phantom types. I don't know of any in the mtl, but this same consideration might be applicable to other libraries with "arbitrary" instances.

Nicolas Frisby schrieb:
This mtl instance is in conflict with a usage of shift/reset that I'm porting from ML (Sheard's type-directed partial evaluator).
instance (MonadReader r' m) => MonadReader r' (ContT r m) where local f m = ContT $ \c -> do r <- ask local f (runContT m (local (const r) . c))
I need to remove the "local (const r)" bit. I think this is just a different point in the diverse design space of dynamic binding/delimited control. Since Haskell lacks visibility management for importing instances, I'm forced to duplicate the rest of the ContT code in order to replace this instance with my own.
You may define a newtype, lift all required instances with -XGeneralizedNewtypeDeriving and define your own MonadReader instance.
1) I am hoping this list can recall the reason for putting this design decision into the mtl. Perhaps there's a reason to prefer this particular side-effect interaction.
2) I am suggesting that the mtl be restructured such that this instance be delegated to a separate module so that i) I can avoid importing it along with the other useful instances ii) and the mtl could provide alternative instances. An alternative to breaking out the modules would be to not provide the instance. Another, more radical alternative could involve phantom types.
I don't know of any in the mtl, but this same consideration might be applicable to other libraries with "arbitrary" instances.
It was discussed in length, that different instances for the same class/type pair leads to trouble sooner or later. (keyword "orphan instances") - Just imagine people who import two instances via two different import paths, then the compiler does not know, which instances to use. Instead newtype is the answer.

On Tue, 23 Dec 2008, Henning Thielemann wrote:
Nicolas Frisby schrieb:
This mtl instance is in conflict with a usage of shift/reset that I'm porting from ML (Sheard's type-directed partial evaluator).
instance (MonadReader r' m) => MonadReader r' (ContT r m) where local f m = ContT $ \c -> do r <- ask local f (runContT m (local (const r) . c))
I need to remove the "local (const r)" bit. I think this is just a different point in the diverse design space of dynamic binding/delimited control. Since Haskell lacks visibility management for importing instances, I'm forced to duplicate the rest of the ContT code in order to replace this instance with my own.
You may define a newtype, lift all required instances with -XGeneralizedNewtypeDeriving and define your own MonadReader instance.
I have put this on: http://haskell.org/haskellwiki/Multiple_instances

Nicolas Frisby
instance (MonadReader r' m) => MonadReader r' (ContT r m) where local f m = ContT $ \c -> do r <- ask local f (runContT m (local (const r) . c))
1) I am hoping this list can recall the reason for putting this design decision into the mtl. Perhaps there's a reason to prefer this particular side-effect interaction.
For what it's worth, this interaction is justified in Section 8.4 of: Sheng Liang, Paul Hudak, and Mark Jones. 1995. Monad transformers and modular interpreters. In POPL '95: Conference record of the annual ACM symposium on principles of programming languages, 333-343. New York: ACM Press. http://web.cecs.pdx.edu/~mpj/pubs/modinterp.html -- Edit this signature at http://www.digitas.harvard.edu/cgi-bin/ken/sig 100 Days to close Guantanamo and end torture http://100dayscampaign.org/

Thanks for the pointer.
I did come across that. They only call this particular lifting "not
natural," which doesn't seem much of a justification - unless they
mean natural in a more formal sense that isn't immediately obvious to
me.
I revisited that paper upon finding the reference in your Delimited
Dynamic Binding, the examples of which finally lent some clarity to
the differences in the computational behaviors I was dealing with.
On Sat, Dec 27, 2008 at 5:14 PM, Chung-chieh Shan
Nicolas Frisby
wrote in article <5ce89fb50812221019p49bdf464rc78c1cdade5099ed@mail.gmail.com> in gmane.comp.lang.haskell.libraries: instance (MonadReader r' m) => MonadReader r' (ContT r m) where local f m = ContT $ \c -> do r <- ask local f (runContT m (local (const r) . c))
1) I am hoping this list can recall the reason for putting this design decision into the mtl. Perhaps there's a reason to prefer this particular side-effect interaction.
For what it's worth, this interaction is justified in Section 8.4 of: Sheng Liang, Paul Hudak, and Mark Jones. 1995. Monad transformers and modular interpreters. In POPL '95: Conference record of the annual ACM symposium on principles of programming languages, 333-343. New York: ACM Press. http://web.cecs.pdx.edu/~mpj/pubs/modinterp.html
-- Edit this signature at http://www.digitas.harvard.edu/cgi-bin/ken/sig 100 Days to close Guantanamo and end torture http://100dayscampaign.org/
_______________________________________________ Libraries mailing list Libraries@haskell.org http://www.haskell.org/mailman/listinfo/libraries

Nicolas Frisby
I did come across that. They only call this particular lifting "not natural," which doesn't seem much of a justification - unless they mean natural in a more formal sense that isn't immediately obvious to me.
I agree it's not much of a justification. Maybe what they meant by "natural" is that the same code (using "ask" and "local") should work in the reader monad as in the continuation monad transformer applied to the reader monad. Here's an example to illustrate: import Control.Monad.Cont import Control.Monad.Reader example local = do x <- ask y <- local (1+) ask z <- ask return (x,y,z) local' f m = ContT $ \c -> do r <- ask local f (runContT m c) In ghci 6.8.2, I get *Main> runReader (runContT (example local) return) 1 (1,2,1) *Main> runReader (runContT (example local') return) 1 (1,2,2)
I revisited that paper upon finding the reference in your Delimited Dynamic Binding, the examples of which finally lent some clarity to the differences in the computational behaviors I was dealing with.
Thanks for the encouragement. (: -- Edit this signature at http://www.digitas.harvard.edu/cgi-bin/ken/sig 100 Days to close Guantanamo and end torture http://100dayscampaign.org/
participants (4)
-
Chung-chieh Shan
-
Henning Thielemann
-
Henning Thielemann
-
Nicolas Frisby