
Do changes to the 'transformers' package fall under the purview of the libraries change control process? -Edward

On Wed, Jan 26, 2011 at 2:20 PM, Edward Kmett
Do changes to the 'transformers' package fall under the purview of the libraries change control process?
From what I understand, no. The package has a maintainer.
As it is a widely used package, and an integral part of the Platform, it wouldn't be a bad idea to put out a request for comments on an idea you have. Antoine

Fair enough.
These are the observations I have about 'transformers'.
1.) Data.Functor.Product admits a useful monad instance.
Monads do not always have coproducts, but their products are well defined. I
rather need this instance for my 'graphs' package, and it can't be defined
outside of transformers without orphans.
instance (Monad m, Monad n) => Monad (Product f g) where
return a = Pair a a
Pair m n >>= f = Pair (m >>= fstP . f) (n >>= sndP . f) where
fstP (Pair a _) = a
sndP (Pair _ b) = b
However, despite the kind signature, this is not an actual MonadTrans
instance.
2.) The instance for Applicative for MaybeT doesn't really follow the other
instances for Applicative in transformers.
instance Applicative m => Applicative (MaybeT m) where
pure = MaybeT . pure . Just
f <*> a = MaybeT $ (<*>) <$> runMaybeT f <*> runMaybeT a
would instead follow the convention that Applicatives depend only on
Applicatives where possible, as opposed to the current default instance.
This convention is followed everywhere in transformers except this module.
3.) Data.Functor.Constant is rather redundant given the existence of the
identical Const functor in Control.Applicative (which is a module that
Data.Functor.Constant depends upon!) The main thing that would need to be
fixed is the addition of Traversable and Foldable to
Control.Applicative.Const, which shouldn't be all that controversial. I'll
propose that more formally separately, as that does directly fall under the
libraries processes.
4.) For completeness, I'm including this here, although I sent it in a
separate email to Ross the other day. A large number of the monad
transformers admit reasonable definitions for Foldable/Traversable. This is
particularly important for IdentityT, but also quite useful for MaybeT and
ListT, etc. and follows logically from my earlier proposal to add the
missing Traversable instances for many of the corresponding Prelude data
types and the fact that other data types in the transformers package provide
Foldable/Traversable instances.
instance Foldable f => Foldable (IdentityT f) where
foldMap f (IdentityT a) = foldMap f a
instance Traversable f => Traversable (IdentityT f) where
traverse f (IdentityT a) = IdentityT <$> traverse f a
instance Foldable f => Foldable (Strict.WriterT w f) where
foldMap f (Strict.WriterT a) = foldMap (f . fst) a
instance Traversable f => Traversable (Strict.WriterT w f) where
traverse f (Strict.WriterT a) = Strict.WriterT <$> traverse f' a where
f' (a, b) = fmap (\c -> (c, b)) (f a)
instance Foldable f => Foldable (Lazy.WriterT w f) where
foldMap f (Lazy.WriterT a) = foldMap (f . fst) a
instance Traversable f => Traversable (Lazy.WriterT w f) where
traverse f (Lazy.WriterT a) = Lazy.WriterT <$> traverse f' a where
f' (a, b) = fmap (\c -> (c, b)) (f a)
instance Foldable f => Foldable (ErrorT e f) where
foldMap f (ErrorT a) = foldMap (foldMap f) a
instance Traversable f => Traversable (ErrorT e f) where
traverse f (ErrorT a) = ErrorT <$> traverse (traverse f) a
instance Foldable f => Foldable (MaybeT f) where
foldMap f (MaybeT a) = foldMap (foldMap f) a
instance Traversable f => Traversable (MaybeT f) where
traverse f (MaybeT a) = MaybeT <$> traverse (traverse f) a
instance Foldable f => Foldable (ListT f) where
foldMap f (ListT a) = foldMap (foldMap f) a
instance Traversable f => Traversable (ListT f) where
traverse f (ListT a) = ListT <$> traverse (traverse f) a
-Edward Kmett
On Wed, Jan 26, 2011 at 3:28 PM, Antoine Latter
Do changes to the 'transformers' package fall under the purview of
On Wed, Jan 26, 2011 at 2:20 PM, Edward Kmett
wrote: the libraries change control process? From what I understand, no. The package has a maintainer.
As it is a widely used package, and an integral part of the Platform, it wouldn't be a bad idea to put out a request for comments on an idea you have.
Antoine

On Wed, Jan 26, 2011 at 3:56 PM, Edward Kmett
Fair enough. These are the observations I have about 'transformers'.
2.) The instance for Applicative for MaybeT doesn't really follow the other instances for Applicative in transformers. instance Applicative m => Applicative (MaybeT m) where pure = MaybeT . pure . Just f <*> a = MaybeT $ (<*>) <$> runMaybeT f <*> runMaybeT a would instead follow the convention that Applicatives depend only on Applicatives where possible, as opposed to the current default instance. This convention is followed everywhere in transformers except this module.
Then you no longer have (<*>) = ap.
This convention is also not followed for the StateT and ErrorT
transformers for the same reason.
If you want something like MaybeT f whose Applicative instance depends
only on the Applicative instance of f, try Compose f Maybe.
--
Dave Menendez

On Wed, Jan 26, 2011 at 5:00 PM, David Menendez
On Wed, Jan 26, 2011 at 3:56 PM, Edward Kmett
wrote: Fair enough. These are the observations I have about 'transformers'.
2.) The instance for Applicative for MaybeT doesn't really follow the other instances for Applicative in transformers. instance Applicative m => Applicative (MaybeT m) where pure = MaybeT . pure . Just f <*> a = MaybeT $ (<*>) <$> runMaybeT f <*> runMaybeT a would instead follow the convention that Applicatives depend only on Applicatives where possible, as opposed to the current default instance. This convention is followed everywhere in transformers except this module.
Then you no longer have (<*>) = ap.
This convention is also not followed for the StateT and ErrorT transformers for the same reason.
If you want something like MaybeT f whose Applicative instance depends only on the Applicative instance of f, try Compose f Maybe.
By that reasoning the existing Applicative instances for WriterT and ListT would be wrong in mtl as well. StateT actually requires a monad, because the s -> m (a, s) monad is given rise to by sandwiching a monad in an adjunction, which isn't sufficient to yield an Applicative instance, but the ErrorT and MaybeT can both perfectly well get by on the weaker Applicative requirement, because they simply compose. The consistency of ap and (<*>) is assumed universally. Applicative merely allows you to optimize this case. -Edward Kmett

On Wed, Jan 26, 2011 at 5:14 PM, Edward Kmett
On Wed, Jan 26, 2011 at 5:00 PM, David Menendez
wrote: On Wed, Jan 26, 2011 at 3:56 PM, Edward Kmett
wrote: Fair enough. These are the observations I have about 'transformers'.
2.) The instance for Applicative for MaybeT doesn't really follow the other instances for Applicative in transformers. instance Applicative m => Applicative (MaybeT m) where pure = MaybeT . pure . Just f <*> a = MaybeT $ (<*>) <$> runMaybeT f <*> runMaybeT a would instead follow the convention that Applicatives depend only on Applicatives where possible, as opposed to the current default instance. This convention is followed everywhere in transformers except this module.
Then you no longer have (<*>) = ap.
This convention is also not followed for the StateT and ErrorT transformers for the same reason.
If you want something like MaybeT f whose Applicative instance depends only on the Applicative instance of f, try Compose f Maybe.
By that reasoning the existing Applicative instances for WriterT and ListT would be wrong in mtl as well. StateT actually requires a monad, because the s -> m (a, s) monad is given rise to by sandwiching a monad in an adjunction, which isn't sufficient to yield an Applicative instance, but the ErrorT and MaybeT can both perfectly well get by on the weaker Applicative requirement, because they simply compose. The consistency of ap and (<*>) is assumed universally. Applicative merely allows you to optimize this case.
In the case of MaybeT, it isn't an optimization. It's a different
function. Using your proposed definition, we get:
*Main> flip runState 0 . runMaybeT $ mzero <*> lift (put 1)
(Nothing,1)
*Main> flip runState 0 . runMaybeT $ mzero `ap` lift (put 1)
(Nothing,0)
--
Dave Menendez

On Wed, Jan 26, 2011 at 6:10 PM, David Menendez
In the case of MaybeT, it isn't an optimization. It's a different function. Using your proposed definition, we get:
*Main> flip runState 0 . runMaybeT $ mzero <*> lift (put 1) (Nothing,1) *Main> flip runState 0 . runMaybeT $ mzero `ap` lift (put 1) (Nothing,0)
Good catch. -Edward

On Wed, Jan 26, 2011 at 03:56:12PM -0500, Edward Kmett wrote:
1.) Data.Functor.Product admits a useful monad instance.
Yes (once the typos are fixed), and it's compatible with the Applicative instance. While we're at it, we might as well add the MonadPlus and MonadFix instances: instance (MonadPlus f, MonadPlus g) => MonadPlus (Product f g) where mzero = Pair mzero mzero Pair x1 y1 `mplus` Pair x2 y2 = Pair (x1 `mplus` x2) (y1 `mplus` y2) instance (MonadFix f, MonadFix g) => MonadFix (Product f g) where mfix f = Pair (mfix (fstP . f)) (mfix (sndP . f)) where fstP (Pair a _) = a sndP (Pair _ b) = b
2.) The instance for Applicative for MaybeT doesn't really follow the other instances for Applicative in transformers.
David Menendez has explained that this is needed for compatibility with the Monad instance.
3.) Data.Functor.Constant is rather redundant given the existence of the identical Const functor in Control.Applicative (which is a module that Data.Functor.Constant depends upon!) The main thing that would need to be fixed is the addition of Traversable and Foldable to Control.Applicative.Const, which shouldn't be all that controversial. I'll propose that more formally separately, as that does directly fall under the libraries processes.
Yes, that was an oversight, considering I wrote both instances. I think it's better not to abbreviate the name, so I'd suggest moving Constant into Control.Applicative (re-exported by Data.Functor.Constant) and deprecating Const. (Or just hiding Const in Control.Applicative.)
4.) For completeness, I'm including this here, although I sent it in a separate email to Ross the other day. A large number of the monad transformers admit reasonable definitions for Foldable/Traversable.
These all look good, though let's write the ErrorT ones as instance (Foldable f) => Foldable (ErrorT e f) where foldMap f (ErrorT a) = foldMap (either (const mempty) f) a instance (Traversable f) => Traversable (ErrorT e f) where traverse f (ErrorT a) = ErrorT <$> traverse (either (pure . Left) (fmap Right . f)) a so we don't have to wait for the Either instances to appear in base. 5.) While we're talking about transformers, do you have an opinion on #4517: Add Data.Functor.Backwards to transformers (http://www.haskell.org/pipermail/libraries/2010-December/015296.html)? It's just a choice between designs using one functor or two.
participants (4)
-
Antoine Latter
-
David Menendez
-
Edward Kmett
-
Ross Paterson