class default method proposal

I'd just like to float an idea that's related to the Class Alias proposal[1] but is perhaps somewhat simpler. We all know that Functor should have been a superclass of Monad, and indeed we now know that Applicative should be too. Making such a change would break lots of things however so the change does not happen. However in this case the Monad operations can be used to implement the Functor and Applicative class methods. So it would be nice if we could get them for free if the author did not choose to write the Functor and Applicative instances. So my suggestion is that we let classes declare default implementations of methods from super-classes. class Functor m => Monad m where {- the ordinary bits -} fmap f m = m >>= return . f So if there already is a Functor instance for m then the default implementation of fmap is not used. Does this proposal have any unintended consequences? I'm not sure. Please discuss :-) Duncan [1] http://repetae.net/recent/out/classalias.html

On Tue, Dec 11, 2007 at 02:20:52PM +0000, Duncan Coutts wrote:
I'd just like to float an idea that's related to the Class Alias proposal[1] but is perhaps somewhat simpler.
We all know that Functor should have been a superclass of Monad, and indeed we now know that Applicative should be too. Making such a change would break lots of things however so the change does not happen.
However in this case the Monad operations can be used to implement the Functor and Applicative class methods. So it would be nice if we could get them for free if the author did not choose to write the Functor and Applicative instances.
So my suggestion is that we let classes declare default implementations of methods from super-classes.
class Functor m => Monad m where {- the ordinary bits -}
fmap f m = m >>= return . f
So if there already is a Functor instance for m then the default implementation of fmap is not used.
Does this proposal have any unintended consequences? I'm not sure. Please discuss :-)
Duncan
This is almost exactly the http://haskell.org/haskellwiki/Class_system_extension_proposal; that page has some discussion of implementation issues. Stefan

On Tue, 2007-12-11 at 07:07 -0800, Stefan O'Rear wrote:
This is almost exactly the http://haskell.org/haskellwiki/Class_system_extension_proposal; that page has some discussion of implementation issues.
Oh yes, so it is. Did this proposal get discussed on any mailing list? I'd like to see what people thought. Was there any conclusion about feasibility? Duncan

Duncan Coutts wrote:
On Tue, 2007-12-11 at 07:07 -0800, Stefan O'Rear wrote:
This is almost exactly the http://haskell.org/haskellwiki/Class_system_extension_proposal; that page has some discussion of implementation issues.
Oh yes, so it is. Did this proposal get discussed on any mailing list? I'd like to see what people thought. Was there any conclusion about feasibility?
Ross proposed this on the libraries list in 2005: http://www.haskell.org//pipermail/libraries/2005-March/003494.html and I brought it up for Haskell': http://www.haskell.org//pipermail/haskell-prime/2006-April/001344.html see also this: http://www.haskell.org//pipermail/haskell-prime/2006-August/001582.html Unfortunately the Haskell' wiki doesn't have a good summary of the issues; it should. I'll add these links at least. Cheers, Simon

On Tue, Dec 11, 2007 at 04:26:52PM +0000, Simon Marlow wrote:
Duncan Coutts wrote:
On Tue, 2007-12-11 at 07:07 -0800, Stefan O'Rear wrote:
This is almost exactly the http://haskell.org/haskellwiki/Class_system_extension_proposal; that page has some discussion of implementation issues.
Oh yes, so it is. Did this proposal get discussed on any mailing list? I'd like to see what people thought. Was there any conclusion about feasibility?
Ross proposed this on the libraries list in 2005:
http://www.haskell.org//pipermail/libraries/2005-March/003494.html
and again in 2003: http://www.haskell.org/pipermail/haskell-cafe/2003-July/004654.html

On Tue, 2007-12-11 at 16:38 +0000, Ross Paterson wrote:
On Tue, Dec 11, 2007 at 04:26:52PM +0000, Simon Marlow wrote:
Duncan Coutts wrote:
On Tue, 2007-12-11 at 07:07 -0800, Stefan O'Rear wrote:
This is almost exactly the http://haskell.org/haskellwiki/Class_system_extension_proposal; that page has some discussion of implementation issues.
Oh yes, so it is. Did this proposal get discussed on any mailing list? I'd like to see what people thought. Was there any conclusion about feasibility?
Ross proposed this on the libraries list in 2005:
http://www.haskell.org//pipermail/libraries/2005-March/003494.html
and again in 2003:
http://www.haskell.org/pipermail/haskell-cafe/2003-July/004654.html
Ross, you need to shout louder! :-) If it really would work ok we should get it fully specified and implemented so we can fix the most obvious class hierarchy problems in a nice backwards compatible way. Things are only supposed to be candidates for Haskell' if they're already implemented. So how about the objection that two sub classes could try and define conflicting defaults for a superclass method? David Menendez had the example of Monad and CoMonad defining Functor's fmap. Can that easily be rejected? I suppose it gives rise to duplicate instance declarations so it'd be an error in the same way that defining clashing instances in two different modules and importing both into a third module. Another error case would be: module A where data Foo module B where instance Functor Foo module C where instance Monad Foo module D import Bar import Baz Now we get slashing instances for Functor, since both Bar and Baz export Functor instances for Foo. Since the instance for Functor Foo was not visible in module C, so we get the default instance defined in C. So the one slightly surprising thing about this suggestion is that we get an instance defined or not depending on whether there is already an instance in scope. In the Functor, Applicative, Monad case I don't see that causing a problem in practise but is it worse more generally? Duncan

| If it really would work ok we should get it fully specified and | implemented so we can fix the most obvious class hierarchy problems in a | nice backwards compatible way. Things are only supposed to be candidates | for Haskell' if they're already implemented. Getting it fully specified is the first thing. Personally I am not keen about a) coupling it to explicit import/export (independently-desirable though such a change might be) b) having instance declarations silently spring into existence Concerning (b) here's a suggestion. As now, require that every instance requires an instance declaration. So, in the main example of http://haskell.org/haskellwiki/Class_system_extension_proposal, for a new data type T you'd write instance Monad T where return = ... (>>=) = ... instance Functor T instance Applicative T The instance declaration for (Functor T) works just as usual (no explicit method, so use the default method) except for one thing: how the default method is found. The change is this: Given "instance C T where ...", for any method 'm' not defined by "...": for every class D of which C is a superclass where there is an instance for (D T) see if the instance gives a binding for 'm' If this search finds exactly one binding, use it, otherwise behave as now This formulation reduces the problem to a more manageable one: a search for the default method. I'm not sure what is supposed to happen if the instance is for something more complicated (T a, say, or multi-parameter type class) but I bet you could work it out. All these instances would need to be in the same module: - you can't define Functor T without Monad T, because you want to pick up the monad-specific default method - you can't define Monad T without Functor T, because the latter is a superclass of the former It still sounds a bit complicated. Simon

Another suggestion, maybe completely off the wall, would be to do something with stand-alone deriving syntax? So instead of "instance Functor T" one might write "deriving Functor for T" ? This might make it more clear that default methods were being brought into play. With this syntax, it would seem appropriate to me that any case of overlap would produce an error (modulo, perhaps, some funny new ghc flag such as -fallow-overlapping-derivations). --s. instance Monad T On Dec 11, 2007, at 12:30 PM, Simon Peyton-Jones wrote:
| If it really would work ok we should get it fully specified and | implemented so we can fix the most obvious class hierarchy problems in a | nice backwards compatible way. Things are only supposed to be candidates | for Haskell' if they're already implemented.
Getting it fully specified is the first thing.
Personally I am not keen about
a) coupling it to explicit import/export (independently-desirable though such a change might be)
b) having instance declarations silently spring into existence
Concerning (b) here's a suggestion. As now, require that every instance requires an instance declaration. So, in the main example of http://haskell.org/haskellwiki/Class_system_extension_proposal, for a new data type T you'd write instance Monad T where return = ... (>>=) = ...
instance Functor T instance Applicative T
The instance declaration for (Functor T) works just as usual (no explicit method, so use the default method) except for one thing: how the default method is found. The change is this: Given "instance C T where ...", for any method 'm' not defined by "...": for every class D of which C is a superclass where there is an instance for (D T) see if the instance gives a binding for 'm' If this search finds exactly one binding, use it, otherwise behave as now
This formulation reduces the problem to a more manageable one: a search for the default method.
I'm not sure what is supposed to happen if the instance is for something more complicated (T a, say, or multi-parameter type class) but I bet you could work it out.
All these instances would need to be in the same module: - you can't define Functor T without Monad T, because you want to pick up the monad-specific default method - you can't define Monad T without Functor T, because the latter is a superclass of the former
It still sounds a bit complicated.
Simon _______________________________________________ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users

What about something like instance Monad MyMonad where (>>=) = ... return = ... deriving ( Functor, Applicative ) That sounds like a friendlier version of SPJ's proposal, in that you no longer have to search for the default method, and every instance is actually manually declared. (I'm not sure if this is what Sterling meant by a stand-alone deriving syntax... it's not really stand-alone, and that's what I like about this idea, it restores locality.) David On Tue, Dec 11, 2007 at 12:59:06PM -0500, Sterling Clover wrote:
Another suggestion, maybe completely off the wall, would be to do something with stand-alone deriving syntax? So instead of "instance Functor T" one might write "deriving Functor for T" ? This might make it more clear that default methods were being brought into play. With this syntax, it would seem appropriate to me that any case of overlap would produce an error (modulo, perhaps, some funny new ghc flag such as -fallow-overlapping-derivations).
--s.
instance Monad T On Dec 11, 2007, at 12:30 PM, Simon Peyton-Jones wrote:
| If it really would work ok we should get it fully specified and | implemented so we can fix the most obvious class hierarchy problems in a | nice backwards compatible way. Things are only supposed to be candidates | for Haskell' if they're already implemented.
Getting it fully specified is the first thing.
Personally I am not keen about
a) coupling it to explicit import/export (independently-desirable though such a change might be)
b) having instance declarations silently spring into existence
Concerning (b) here's a suggestion. As now, require that every instance requires an instance declaration. So, in the main example of http://haskell.org/haskellwiki/Class_system_extension_proposal, for a new data type T you'd write instance Monad T where return = ... (>>=) = ...
instance Functor T instance Applicative T
The instance declaration for (Functor T) works just as usual (no explicit method, so use the default method) except for one thing: how the default method is found. The change is this: Given "instance C T where ...", for any method 'm' not defined by "...": for every class D of which C is a superclass where there is an instance for (D T) see if the instance gives a binding for 'm' If this search finds exactly one binding, use it, otherwise behave as now
This formulation reduces the problem to a more manageable one: a search for the default method.
I'm not sure what is supposed to happen if the instance is for something more complicated (T a, say, or multi-parameter type class) but I bet you could work it out.
All these instances would need to be in the same module: - you can't define Functor T without Monad T, because you want to pick up the monad-specific default method - you can't define Monad T without Functor T, because the latter is a superclass of the former
It still sounds a bit complicated.
Simon

Simon Peyton-Jones wrote:
Given "instance C T where ...", for any method 'm' not defined by "...": for every class D of which C is a superclass where there is an instance for (D T) see if the instance gives a binding for 'm' If this search finds exactly one binding, use it, otherwise behave as now
A better rule would be: If this search finds exactly one binding that is minimal in the partial ordering defined by the superclass hierarchy, use it, otherwise behave as now. Would that be much harder to implement? -Yitz

Simon Peyton-Jones wrote:
Concerning (b) here's a suggestion. As now, require that every instance requires an instance declaration. So, in the main example of http://haskell.org/haskellwiki/Class_system_extension_proposal, for a new data type T you'd write instance Monad T where return = ... (>>=) = ...
instance Functor T instance Applicative T
Another alternative is to allow multiple classes in an instance declaration: instance (Monad T, Functor T, Applicative T) where return = ... (>>=) = ... The advantage is that this makes it more clear where the instances come from, especially if a class has multiple sub classes with different defaults. It also eliminates tricky issues with importing. Of course this needs some (albeit very little) new syntax. I wrote a proposal a while ago, http://haskell.org/haskellwiki/Superclass_defaults Twan

I had it pretty well worked out for single parameter type classes, but I
couldn't see any nice extension to multiple parameters.
On Dec 11, 2007 5:30 PM, Simon Peyton-Jones
| If it really would work ok we should get it fully specified and | implemented so we can fix the most obvious class hierarchy problems in a | nice backwards compatible way. Things are only supposed to be candidates | for Haskell' if they're already implemented.
Getting it fully specified is the first thing.
Personally I am not keen about
a) coupling it to explicit import/export (independently-desirable though such a change might be)
b) having instance declarations silently spring into existence
Concerning (b) here's a suggestion. As now, require that every instance requires an instance declaration. So, in the main example of http://haskell.org/haskellwiki/Class_system_extension_proposal, for a new data type T you'd write instance Monad T where return = ... (>>=) = ...
instance Functor T instance Applicative T
The instance declaration for (Functor T) works just as usual (no explicit method, so use the default method) except for one thing: how the default method is found. The change is this: Given "instance C T where ...", for any method 'm' not defined by "...": for every class D of which C is a superclass where there is an instance for (D T) see if the instance gives a binding for 'm' If this search finds exactly one binding, use it, otherwise behave as now
This formulation reduces the problem to a more manageable one: a search for the default method.
I'm not sure what is supposed to happen if the instance is for something more complicated (T a, say, or multi-parameter type class) but I bet you could work it out.
All these instances would need to be in the same module: - you can't define Functor T without Monad T, because you want to pick up the monad-specific default method - you can't define Monad T without Functor T, because the latter is a superclass of the former
It still sounds a bit complicated.
Simon _______________________________________________ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users

On Dec 11, 2007 9:20 AM, Duncan Coutts
So my suggestion is that we let classes declare default implementations of methods from super-classes.
Does this proposal have any unintended consequences? I'm not sure.
Please discuss :-) It creates ambiguity if two classes declare defaults for a common
superclass.
My standard example involves Functor, Monad, and Comonad. Both Monad and
Comonad could provide a default implementation for fmap. But let's say I
have a type which is both a Monad and a Comonad: which default
implementation gets used?
I'm disappointed to see this objection isn't listed on the wiki.
--
Dave Menendez

David Menendez wrote:
On Dec 11, 2007 9:20 AM, Duncan Coutts
mailto:duncan.coutts@worc.ox.ac.uk> wrote: So my suggestion is that we let classes declare default implementations of methods from super-classes.
Does this proposal have any unintended consequences? I'm not sure. Please discuss :-)
It creates ambiguity if two classes declare defaults for a common superclass.
My standard example involves Functor, Monad, and Comonad. Both Monad and Comonad could provide a default implementation for fmap. But let's say I have a type which is both a Monad and a Comonad: which default implementation gets used?
I'm disappointed to see this objection isn't listed on the wiki.
Doesn't sound like a very big problem. That would just be a compile time error ("More than one default for fmap possible for Foo, please reslve ambiguity"). Jules

Jules Bean wrote:
David Menendez wrote:
Duncan Coutts wrote:
So my suggestion is that we let classes declare default implementations of methods from super-classes.
It creates ambiguity if two classes declare defaults for a common superclass.
My standard example involves Functor, Monad, and Comonad. Both Monad and Comonad could provide a default implementation for fmap. But let's say I have a type which is both a Monad and a Comonad: which default implementation gets used?
I'm disappointed to see this objection isn't listed on the wiki.
Doesn't sound like a very big problem. That would just be a compile time error ("More than one default for fmap possible for Foo, please reslve ambiguity").
And how would you resolve that ambiguity? module Control.Functor.UsefulStuff (hylo) where hylo :: Functor f => (a -> f a) -> (f b -> b) -> a -> b hylo f g = g . fmap (hylo f g) . f module BANG where import Foo (Foo) import Foo.Is.Monad import Foo.Is.Comonad import Control.Functor.UsefulStuff (hylo) bar :: Bar -> Foo Bar baz :: Foo Baz -> Baz bang = hylo bar baz The problem is that the ambiguity may arise by just importing different modules while not having access to the offending call to fmap . Also note that as much as I'd like explicit import/export of type class instances, the current implicit and global export is no accident, it's crucial for well-definedness. See also the second half of http://article.gmane.org/gmane.comp.lang.haskell.general/15471 In other words, the main problem of all those superclass/explicit import/export proposals is that there are no proofs of the fact that they only allow well-defined programs. The homework isn't done yet, discussing adoption is too early. Regards, apfelmus

On Dec 11, 2007 3:19 PM, David Menendez
On Dec 11, 2007 9:20 AM, Duncan Coutts
wrote: So my suggestion is that we let classes declare default implementations of methods from super-classes.
Does this proposal have any unintended consequences? I'm not sure. Please discuss :-)
It creates ambiguity if two classes declare defaults for a common superclass.
My standard example involves Functor, Monad, and Comonad. Both Monad and Comonad could provide a default implementation for fmap. But let's say I have a type which is both a Monad and a Comonad: which default implementation gets used?
Isn't a type which is both a Monad and a Comonad just Identity? (I'm actually not sure, I'm just conjecting) Luke

Luke Palmer wrote:
Isn't a type which is both a Monad and a Comonad just Identity?
(I'm actually not sure, I'm just conjecting)
Good idea, but it's not the case. data L a = One a | Cons a (L a) -- non-empty list -- standard list monad instance Monad L where return = One -- join concatenates all lists in the list join (One y) = y join (Cons (One x) ys) = Cons x (join ys) join (Cons (Cons x xs) ys) = Cons x (join (Cons xs ys)) class Comonad c where counit :: c a -> a cobind :: c a -> (c a -> b) -> c b instance Comonad L where counit (One x) = x -- that's why we insist on non-emptiness counit (Cons x _) = x -- f has access to the whole past cobind ys@(One x) f = One (f ys) cobind ys@(Cons x xs) f = Cons (f ys) (cobind f xs) Checking the laws is left as an exercise for the reader :) Regards, apfelmus

apfelmus wrote:
Luke Palmer wrote:
Isn't a type which is both a Monad and a Comonad just Identity?
(I'm actually not sure, I'm just conjecting)
Good idea, but it's not the case.
data L a = One a | Cons a (L a) -- non-empty list
Maybe I can entice you to elaborate slightly. From http://www.eyrie.org/~zednenem/2004/hsce/Control.Functor.html and Control.Comonad.html there is ---------------------------------------------------------- newtype O f g a -- Functor composition: f `O` g instance (Functor f, Functor g) => Functor (O f g) where ... instance Adjunction f g => Monad (O g f) where ... instance Adjunction f g => Comonad (O f g) where ... -- I assume Haskell can infer Functor (O g f) from Monad (O g f), which -- is why that is missing here? class (Functor f, Functor g) => Adjunction f g | f -> g, g -> f where leftAdjunct :: (f a -> b) -> a -> g b rightAdjunct :: (a -> g b) -> f a -> b ---------------------------------------------------------- Functors are associative but not generally commutative. Apparently a Monad is also a Comonad if there exist left (f) and right (g) adjuncts that commute. [and only if also??? Is there a contrary example of a Monad/Comonad for which no such f and g exist?] In the case of
data L a = One a | Cons a (L a) -- non-empty list
what are the appropriate definitions of leftAdjunct and rightAdjunct? Are they Monad.return and Comonad.extract respectively? That seems to unify a and b unnecessarily. Do they wrap bind and cobind? Are they of any practical utility? My category theory study stopped somewhere between Functor and Adjunction, but is there any deep magic you can describe here in a paragraph or two? I feel like I will never get Monad and Comonad until I understand Adjunction. Dan

Dan Weston wrote:
apfelmus wrote:
Luke Palmer wrote:
Isn't a type which is both a Monad and a Comonad just Identity?
(I'm actually not sure, I'm just conjecting)
Good idea, but it's not the case.
data L a = One a | Cons a (L a) -- non-empty list
Maybe I can entice you to elaborate slightly. From http://www.eyrie.org/~zednenem/2004/hsce/Control.Functor.html and Control.Comonad.html there is
---------------------------------------------------------- newtype O f g a -- Functor composition: f `O` g
instance (Functor f, Functor g) => Functor (O f g) where ... instance Adjunction f g => Monad (O g f) where ... instance Adjunction f g => Comonad (O f g) where ...
-- I assume Haskell can infer Functor (O g f) from Monad (O g f), which -- is why that is missing here?
No. But it can infer Functor (O g f) from instance (Functor f, Functor g) => Functor (O f g), (using 'g' for 'f' and 'f' for 'g').
class (Functor f, Functor g) => Adjunction f g | f -> g, g -> f where leftAdjunct :: (f a -> b) -> a -> g b rightAdjunct :: (a -> g b) -> f a -> b ----------------------------------------------------------
Functors are associative but not generally commutative. Apparently a Monad is also a Comonad if there exist left (f) and right (g) adjuncts that commute. [and only if also??? Is there a contrary example of a Monad/Comonad for which no such f and g exist?]
In the case of
data L a = One a | Cons a (L a) -- non-empty list
what are the appropriate definitions of leftAdjunct and rightAdjunct? Are they Monad.return and Comonad.extract respectively? That seems to unify a and b unnecessarily. Do they wrap bind and cobind? Are they of any practical utility?
I think you're asking the wrong question! The first question needs to be : What is "f" and what is "g" ? What are the two Functors in this case? We know that we want g `O` f to be L, because we know that the unit is return, i.e. One, and unit :: a -> O g f a otherwise known as eta :: a -> O g f a We also know there is a co-unit epsilon :: O f g a -> a, but we don't know much about that until we work out how to decompose L into two Functors. There are two standard ways to decompose a monad into two adjoint functors: the Kleisli decomposition and the Eilenberg-Moore decomposition. However, neither of these categories is a subcategory of Hask in an obvious way, so I don't immediately see how to write "f" and "g" as haskell functors. Maybe someone else can show the way :) Jules

On Dec 14, 2007 5:14 AM, Jules Bean
There are two standard ways to decompose a monad into two adjoint functors: the Kleisli decomposition and the Eilenberg-Moore decomposition.
However, neither of these categories is a subcategory of Hask in an obvious way, so I don't immediately see how to write "f" and "g" as haskell functors.
Maybe someone else can show the way :)
One possibility is to extend Haskell's Functor class. We can define a class of (some) categories whose objects are Haskell types:
class Category mor where id :: mor a a (.) :: mor b c -> mor a b -> mor a c
The instance for (->) should be obvious. We can also define an instance for Kleisli operations:
newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b } instance (Monad m) => Category (Kleisli m) -- omitted
Next, a class for (some) functors between these categories:
class (Category morS, Category morT) => Functor f morS morT where fmap :: morS a b -> morT (f a) (f b)
Unlike the usual Haskell Functor class, this requires us to distinguish the functor itself from the type constructor involved in the functor. Here's an instance converting Kleisli operations to functions.
instance Monad m => Functor m (Kleisli m) (->) where -- fmap :: Kleisli m a b -> (m a -> m b) fmap f = (>>= runKleisli f)
Going the other way is tricker, because our Functor interface requires a type constructor. We'll use Id.
newtype Id a = Id { unId :: a }
instance Monad m => Functor Id (->) (Kleisli m) where -- fmap :: (a -> b) -> Kleisli m (Id a) (Id b) fmap f = Kleisli (return . Id . f . unId)
Finally, adjunctions between functors:
class (Functor f morS morT, Functor g morT morS) => Adjunction f g morS morT | f g morS -> morT, f g morT -> morS where leftAdj :: morT (f a) b -> morS a (g b) rightAdj :: morS a (g b) -> morT (f a) b
The functional dependency isn't really justified. It's there to eliminate ambiguity in the later code. The two functors above are adjoint:
instance (Monad m) => Adjunction Id m (->) (Kleisli m) where -- leftAdj :: Kleisli (Id a) b -> (a -> m b) leftAdj f = runKleisli f . Id
-- rightAdj :: (a -> m b) -> Kleisli (Id a) b rightAdj f = Kleisli (f . unId)
So, given two adjoint functors, we have a monad and a comonad. Note, however, that these aren't necessarily the same as the Haskell classes Monad and Comonad. Here are the monad operations:
unit :: (Adjunction f g morS morT) => morS a (g(f a)) unit = leftAdj id
extend :: (Adjunction f g morS morT) => morS a (g(f b)) -> morS (g(f a)) (g(f b)) extend f = fmap (rightAdj f)
The monad's type constructor is the composition of g and f. Extend corresponds to (>>=) with the arguments reversed. In our running example, unit and extend have these types: unit :: Monad m => a -> m (Id a) extend :: Monad m => (a -> m (Id b)) -> m (Id a) -> m (Id b) This corresponds to our original monad, only with the extra Id. Here are the comonad operations:
counit :: (Adjunction f g morS morT) => morT (f(g a)) a counit = rightAdj id
coextend :: (Adjunction f g morS morT) => morT (f(g a)) b -> morT (f(g a)) (f(g b)) coextend f = fmap (leftAdj f)
In our running example, counit and coextend have these types: counit :: Monad m => Kleisli m (Id (m a)) a coextend :: Monad m => Kleisli m (Id (m a)) b -> Kleisli m (Id (m a)) (Id (m b)) Thus, m is effectively a comonad in its Kleisli category. We can tell a similar story with Comonads and CoKleisli operations, eventually reaching an adjunction like this:
instance (Comonad w) => Adjunction w Id (CoKleisli w) (->) -- omitted
--
Dave Menendez

Dan Weston wrote:
newtype O f g a = O (f (g a)) -- Functor composition: f `O` g
instance (Functor f, Functor g) => Functor (O f g) where ... instance Adjunction f g => Monad (O g f) where ... instance Adjunction f g => Comonad (O f g) where ...
class (Functor f, Functor g) => Adjunction f g | f -> g, g -> f where leftAdjunct :: (f a -> b) -> a -> g b rightAdjunct :: (a -> g b) -> f a -> b ----------------------------------------------------------
Functors are associative but not generally commutative. Apparently a Monad is also a Comonad if there exist left (f) and right (g) adjuncts that commute.
Yes, but that's only sufficient, not necessary. Jules and David already pointed out that while every monad comes from an adjunction, this adjunction usually involves categories different from Hask. So, there are probably no adjoint functors f and g in Hask such that [] ~= g `O` f or
data L a = One a | Cons a (L a) -- non-empty list
L ~= g `O` f (proof?) Yet, both are monads and the latter is even a comonad. Moreover, f and g can only commute if they have the same source and target category (Hask in our case). And even when they don't commute, the resulting monad could still be a comonad, too.
My category theory study stopped somewhere between Functor and Adjunction, but is there any deep magic you can describe here in a paragraph or two? I feel like I will never get Monad and Comonad until I understand Adjunction.
Alas, I wish I could, but I have virtually no clue about adjoint functors myself :) I only know the classic example that conjunction and implication f a = (a,S) g b = S -> b are adjoint (a,S) -> b = a -> (S -> b) which is just well-known currying. We get the state monad (g `O` f) a = S -> (S,a) and the stream comonad (f `O` f) a = (S, S -> a) out of that. Regards apfelmus

On Sun, 2007-12-16 at 13:49 +0100, apfelmus wrote:
Dan Weston wrote:
newtype O f g a = O (f (g a)) -- Functor composition: f `O` g
instance (Functor f, Functor g) => Functor (O f g) where ... instance Adjunction f g => Monad (O g f) where ... instance Adjunction f g => Comonad (O f g) where ...
class (Functor f, Functor g) => Adjunction f g | f -> g, g -> f where leftAdjunct :: (f a -> b) -> a -> g b rightAdjunct :: (a -> g b) -> f a -> b ----------------------------------------------------------
Functors are associative but not generally commutative. Apparently a Monad is also a Comonad if there exist left (f) and right (g) adjuncts that commute.
Yes, but that's only sufficient, not necessary.
Jules and David already pointed out that while every monad comes from an adjunction, this adjunction usually involves categories different from Hask. So, there are probably no adjoint functors f and g in Hask such that
[] ~= g `O` f
or
data L a = One a | Cons a (L a) -- non-empty list
L ~= g `O` f
(proof?) Yet, both are monads and the latter is even a comonad.
Moreover, f and g can only commute if they have the same source and target category (Hask in our case). And even when they don't commute, the resulting monad could still be a comonad, too.
My category theory study stopped somewhere between Functor and Adjunction, but is there any deep magic you can describe here in a paragraph or two? I feel like I will never get Monad and Comonad until I understand Adjunction.
Alas, I wish I could, but I have virtually no clue about adjoint functors myself :)
Learn about representability. Representability is the core of category theory. (Though, of course, it is closely related to adjunctions.)
I only know the classic example that conjunction and implication
f a = (a,S) g b = S -> b
are adjoint
(a,S) -> b = a -> (S -> b)
which is just well-known currying. We get the state monad
(g `O` f) a = S -> (S,a)
and the stream comonad
(f `O` f) a = (S, S -> a)
out of that.
There is another very closely related adjunction that is less often mentioned. ((-)->C)^op -| (-)->C or a -> b -> C ~ b -> a -> C This gives rise to the monad, M a = (a -> C) -> C this is also exactly the comonad it gives rise to (in the op category which ends up being the above monad in the "normal" category).

Derek Elkins wrote:
There is another very closely related adjunction that is less often mentioned.
((-)->C)^op -| (-)->C or a -> b -> C ~ b -> a -> C
This gives rise to the monad, M a = (a -> C) -> C this is also exactly the comonad it gives rise to (in the op category which ends up being the above monad in the "normal" category).
That looks very like the type of mfix. Is this related to MonadFix? Thanks, Yitz

On Dec 17, 2007 4:34 AM, Yitzchak Gale
Derek Elkins wrote:
There is another very closely related adjunction that is less often mentioned.
((-)->C)^op -| (-)->C or a -> b -> C ~ b -> a -> C
This gives rise to the monad, M a = (a -> C) -> C this is also exactly the comonad it gives rise to (in the op category which ends up being the above monad in the "normal" category).
That looks very like the type of mfix. Is this related to MonadFix?
I think that's the continuation monad.
--
Dave Menendez

On Mon, 2007-12-17 at 09:58 -0500, David Menendez wrote:
On Dec 17, 2007 4:34 AM, Yitzchak Gale
wrote: Derek Elkins wrote: > There is another very closely related adjunction that is less often > mentioned. > > ((-)->C)^op -| (-)->C > or > a -> b -> C ~ b -> a -> C > > This gives rise to the monad, > M a = (a -> C) -> C > this is also exactly the comonad it gives rise to (in the op category > which ends up being the above monad in the "normal" category). That looks very like the type of mfix. Is this related to MonadFix?
I think that's the continuation monad.
Let's do some (more) category theory. The unit of an adjunction (which is the unit of the monad it gives rise to) is the image of id in the isomorphism of hom-sets that defines an adjunction. In this case that isomorphism is (a -> b -> c) ~ (b -> a -> c), and the type completely determines the implementation (if you don't immediately recognize it), the isomorphism (in both ways actually) is flip. So the unit of the adjunction (return) is flip id and indeed that is exactly what Cont's definition of return is. (a -> C) is a contravariant functor in a, so class ContraFunctor f where cofmap :: (b -> a) -> f a -> f b instance ContraFunctor (-> c) where -- not legal cofmap f = (. f) This obviously satisfies the (contravariant) functor laws, cofmap id = id and cofmap (f . g) = cofmap g . cofmap f instance Functor M where fmap = cofmap . cofmap This obviously satisfies the functor laws, fmap id = id and fmap (f . g) = fmap f . fmap g join is then join :: M (M a) -> M a join = cofmap (flip id) -- flip id is also the counit This implementation is also forced by the types. (>>=) then has it's standard definition in terms of join and fmap, m >>= f = join (fmap f m) and if this is expanded out, this is indeed seen to be the implementation of (>>=) for the continuation monad.

Duncan Coutts
We all know that Functor should have been a superclass of Monad, and indeed we now know that Applicative should be too. Making such a change would break lots of things however so the change does not happen.
However in this case the Monad operations can be used to implement the Functor and Applicative class methods. So it would be nice if we could get them for free if the author did not choose to write the Functor and Applicative instances.
...
Does this proposal have any unintended consequences? I'm not sure. Please discuss :-)
I started a discussion on these lines on the Haskell prime mailing list a while ago. Apart from the objections others have posted, I think just supplying methods a bit unstructured. In http://article.gmane.org/gmane.comp.lang.haskell.prime/1578/match=all+monads... I suggested instead that such defaults should be instance declarations. It gives much the same effect (and with similar problems), but makes it clear to which class the methods being declared belong. If you were to find a middle ground (say using the syntax I suggest there but with the interpretation that it supplies default methods for the superclass, and use Simons suggestion of requiring explicit empty instance declarations when you want to use them, we might get somewhere sensible. -- Jón Fairbairn Jon.Fairbairn@cl.cam.ac.uk
participants (17)
-
apfelmus
-
Dan Weston
-
David Menendez
-
David Roundy
-
Derek Elkins
-
Duncan Coutts
-
Jon Fairbairn
-
Jules Bean
-
Lennart Augustsson
-
Luke Palmer
-
Ross Paterson
-
Simon Marlow
-
Simon Peyton-Jones
-
Stefan O'Rear
-
Sterling Clover
-
Twan van Laarhoven
-
Yitzchak Gale