upgrading mtl1 to mtl2

I just got started on this because packages are starting to use mtl2. I had hoped it would be simple, and hopefully it is, but... Do I really have to add (Functor m) to the 300 or so functions with (Monad m) on them? Or just not use fmap or applicative? Ow. Not what I would call "small incompatibilities." Is there a more graceful way to get around this? So I thought if it's going to be this much of a hassle I might as well just port to transformers, which I gather is supposed to be the future anyway. But transformers is lacking the classes, and I gather they're in monads-tf and monads-fd. But monads-fd says it's now deprecated because of the existence of mtl-2. So what's the story? Has transformers now turned around and been deprecated in favor of mtl-2? Should I just keep using mtl but port to mtl-2? Or is there some other non-obsolete package for transformers? This page: http://haskell.org/haskellwiki/Monad_Transformers Says that the only benefit of 'transformers' is that it's "haskell 98 and thus more portable," but doesn't that come with the caveat that "only if you don't use classes and do all the lifting manually"?

On 16 February 2011 14:46, Evan Laforge
I just got started on this because packages are starting to use mtl2. I had hoped it would be simple, and hopefully it is, but...
Do I really have to add (Functor m) to the 300 or so functions with (Monad m) on them? Or just not use fmap or applicative?
Use liftM instead of fmap if all of your functions are polymorphic in the type of Monad and you don't want to bother putting in the Functor constraint.
So I thought if it's going to be this much of a hassle I might as well just port to transformers, which I gather is supposed to be the future anyway. But transformers is lacking the classes, and I gather they're in monads-tf and monads-fd. But monads-fd says it's now deprecated because of the existence of mtl-2. So what's the story? Has transformers now turned around and been deprecated in favor of mtl-2?
As I understand it: * monads-fd originally started as mtl-1 but with all the Haskell98 stuff put into a separate package called transformers (so that those that don't need/want extensions can do so). * mtl-2 is just a renamed monads-fd (which has since had some changes compared to mtl-1). * monads-tf is a Type Family version of mtl-2/monads-fd
Should I just keep using mtl but port to mtl-2? Or is there some other non-obsolete package for transformers? This page:
http://haskell.org/haskellwiki/Monad_Transformers
Says that the only benefit of 'transformers' is that it's "haskell 98 and thus more portable," but doesn't that come with the caveat that "only if you don't use classes and do all the lifting manually"?
Yes, but some people don't want the extensions. There are a few other monad transformer implementations; have a look through http://hackage.haskell.org/package/#cat:monads -- Ivan Lazar Miljenovic Ivan.Miljenovic@gmail.com IvanMiljenovic.wordpress.com

On Tue, Feb 15, 2011 at 7:58 PM, Ivan Lazar Miljenovic
On 16 February 2011 14:46, Evan Laforge
wrote: I just got started on this because packages are starting to use mtl2. I had hoped it would be simple, and hopefully it is, but...
Do I really have to add (Functor m) to the 300 or so functions with (Monad m) on them? Or just not use fmap or applicative?
Use liftM instead of fmap if all of your functions are polymorphic in the type of Monad and you don't want to bother putting in the Functor constraint.
There are hundreds of 'fmap's in there too. And even more <$>s and a scattering of <*>s. And... these functions are convenient! I like using them. If "upgrading" to mtl2 means porting away from Functor and Applicative... then that's even less of a "minor incompatibility"! At least the class context mangling can be mostly done with search and replace.
Says that the only benefit of 'transformers' is that it's "haskell 98 and thus more portable," but doesn't that come with the caveat that "only if you don't use classes and do all the lifting manually"?
Yes, but some people don't want the extensions.
Ok, so does that mean the migration to transformers so mtl could be deleted is cancelled? Or was rendered obsolete by mtl-2? Or maybe I was imagining it? I think minimal extensions is a worthy goal, but you can hardly position Y as "the next step" for X when all it is is X minus features that people still like...

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On 2/16/11 00:51 , Evan Laforge wrote:
On Tue, Feb 15, 2011 at 7:58 PM, Ivan Lazar Miljenovic
wrote: On 16 February 2011 14:46, Evan Laforge
wrote: I just got started on this because packages are starting to use mtl2. I had hoped it would be simple, and hopefully it is, but...
Do I really have to add (Functor m) to the 300 or so functions with (Monad m) on them? Or just not use fmap or applicative?
Use liftM instead of fmap if all of your functions are polymorphic in the type of Monad and you don't want to bother putting in the Functor constraint.
There are hundreds of 'fmap's in there too. And even more <$>s and a scattering of <*>s. And... these functions are convenient! I like using them. If "upgrading" to mtl2 means porting away from Functor and Applicative... then that's even less of a "minor incompatibility"! At least the class context mangling can be mostly done with search and replace.
Says that the only benefit of 'transformers' is that it's "haskell 98 and thus more portable," but doesn't that come with the caveat that "only if you don't use classes and do all the lifting manually"?
Yes, but some people don't want the extensions.
Ok, so does that mean the migration to transformers so mtl could be deleted is cancelled? Or was rendered obsolete by mtl-2? Or maybe I was imagining it? I think minimal extensions is a worthy goal, but you can hardly position Y as "the next step" for X when all it is is X minus features that people still like...
Yes; as I understand it, mtl2 is transformers + monad-fd, and the standard upgrade path is to go to mtl2. monads-tf is expected to eventually replace monads-fd, but it's not a near future change (for one thing, type families are still being figured out; see the recent thread about injective type functions as an example). It was decided that the mtl2 route would be easier than forcing people to replace mtl with transofrmers + monads-fd. - -- brandon s. allbery [linux,solaris,freebsd,perl] allbery.b@gmail.com system administrator [openafs,heimdal,too many hats] kf8nh -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (Darwin) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/ iEYEARECAAYFAk1dtWMACgkQIn7hlCsL25WPFwCaA8MbZYbZr/gQ9pa/ttj/E5a0 pTgAoIZ9LMNuq4o7arXmTUZa5qTUFjx4 =NLta -----END PGP SIGNATURE-----

On Tue, Feb 15, 2011 at 07:46:29PM -0800, Evan Laforge wrote:
Do I really have to add (Functor m) to the 300 or so functions with (Monad m) on them? Or just not use fmap or applicative?
If you're using Monad m to get Functor or Applicative instances for a functor built from m, then I'm afraid you will need to add (Functor m) or (Applicative m) to the constraints in most cases.
So I thought if it's going to be this much of a hassle I might as well just port to transformers, which I gather is supposed to be the future anyway. But transformers is lacking the classes, and I gather they're in monads-tf and monads-fd. But monads-fd says it's now deprecated because of the existence of mtl-2. So what's the story? Has transformers now turned around and been deprecated in favor of mtl-2?
monads-fd is deprecated is favour of mtl-2, but transformers isn't. Now you have a choice: use the portable transformers or the functional dependencies of mtl. Either choice is compatible with other packages, because both ultimately rest on transformers.
http://haskell.org/haskellwiki/Monad_Transformers
Says that the only benefit of 'transformers' is that it's "haskell 98 and thus more portable," but doesn't that come with the caveat that "only if you don't use classes and do all the lifting manually"?
Good point -- I've added such a caveat.

On Wed, Feb 16, 2011 at 7:01 AM, Ross Paterson
On Tue, Feb 15, 2011 at 07:46:29PM -0800, Evan Laforge wrote:
Do I really have to add (Functor m) to the 300 or so functions with (Monad m) on them? Or just not use fmap or applicative?
If you're using Monad m to get Functor or Applicative instances for a functor built from m, then I'm afraid you will need to add (Functor m) or (Applicative m) to the constraints in most cases.
I've noticed another solution, which is to make a class M with Applicative and Monad as superclasses, and put the lifted basic operations into that, then instead of '(Functor m, Monad m) => MyM m a', write '(M m) => m a'. I already do this same thing in a different place so I can get unlifted access from another transformer stack. It makes nicer looking signatures, but it's more complicated and less direct. Whenever it comes time to play games like that I have to do a fair amount of trial and error because I don't understand typeclasses or the type errors produced to get it right the first time. And I suppose if I can barely write it, I shouldn't, but I know other people understand this stuff a lot better than I do. Are other people using tricks like this? Or will there just be massive signature rewriting in the wake of mtl2? Or maybe others don't use monads so extensively?

On Thu, Feb 17, 2011 at 11:32 AM, Evan Laforge
Or will there just be massive signature rewriting in the wake of mtl2?
I must admit I still don't understand your exact problem. Could you help me with an example where using mtl2 requires an additional (Functor m) constraint that is not required when using mtl1? Thanks, Sebastian

On Wed, Feb 16, 2011 at 11:28 PM, Sebastian Fischer
On Thu, Feb 17, 2011 at 11:32 AM, Evan Laforge
wrote: Or will there just be massive signature rewriting in the wake of mtl2?
I must admit I still don't understand your exact problem. Could you help me with an example where using mtl2 requires an additional (Functor m) constraint that is not required when using mtl1?
Sure. It could be I'm doing it wrong too, since I'm far from sure of myself here, and that's why I posted in the first place. Say you have a widely used monad: type CmdStack m = ThisT (ThatT m) newtype CmdT m a = CmdT (CmdStack m a) deriving (Everything) run_cmd_t (CmdT x) = x abort :: (Monad m) => CmdT m a abort = Error.throwError State.Abort f1 :: (Monad m) => CmdT m X f1 = fmap ... ... Follows are many modules full of functions that look like 'f1', or transitively call 'f1'. It used to be that (Monad m) implied (Functor m) but now evidently that is no longer so (the error is 'Could not deduce (Functor m) from the context (Monad m)'). The result is that every function that touches an fmap-using function needs a (Functor m) constraint. It goes for Applicative too, but evidently Applicative has Functor as a superclass, so adding just one is enough. It's even more fun because many functions may not require (Functor m)... but if you add an 'fmap' or <$> to something other functions call, I'm guessing there will be a ripple effect where you have to add Functor to every function that transitively calls it. So now when writing new functions you can choose between the wordy safe way or the concise way which might work now but break later. I don't really understand what benefit I get from the change, but hopefully it's significant because the signatures do get significantly uglier and it affects every single one. And since there's no way to abstract over class contexts aside from declaring a dummy class, there's no way that I know of to insulate from changes like this.

On 17 February 2011 07:28, Sebastian Fischer
I must admit I still don't understand your exact problem. Could you help me with an example where using mtl2 requires an additional (Functor m) constraint that is not required when using mtl1?
I think the problem is that the mtl1 Functor instances looked like: instance Monad m => Functor (ReaderT e m) where fmap = ... But the mtl2/transformers instances look like: instance Functor f => Functor (ReaderT e f) where fmap = ... This is overall an improvement, but it does mean that if you relied on getting "fmap" for e.g. a (ReaderT e m) monad from a (Monad m) constraint with mtl1 then your code is now broken. You need to add (Functor m) to your context for mtl2. Naturally, this would not be a problem if Functor were a Monad superclass.. Cheers, Max

On Thu, Feb 17, 2011 at 4:57 PM, Max Bolingbroke wrote: I think the problem is that the mtl1 Functor instances looked like: instance Monad m => Functor (ReaderT e m) where
fmap = ... But the mtl2/transformers instances look like: instance Functor f => Functor (ReaderT e f) where
fmap = ... I see! So the problem is not that previously "(Monad m) implied (Functor m)"
(which has been proposed only recently) but that previously many Functor
instances required (Monad m) but now require (Functor m).
Thanks for the clarification,
Sebastian
participants (6)
-
Brandon S Allbery KF8NH
-
Evan Laforge
-
Ivan Lazar Miljenovic
-
Max Bolingbroke
-
Ross Paterson
-
Sebastian Fischer