[Haskell-cafe] Functor => Applicative => Monad

mokus at deepbondi.net mokus at deepbondi.net
Thu Dec 23 16:13:13 CET 2010


On 22/12/2010 19:03, Simon Marlow wrote:
> On 14/12/2010 08:35, Isaac Dupree wrote:
>> On 12/14/10 03:13, John Smith wrote:
>>> I would like to formally propose that Monad become a subclass of
>>> Applicative, with a call for consensus by 1 February. The change is
>>> described on the wiki at
>>> http://haskell.org/haskellwiki/Functor-Applicative-Monad_Proposal,
>>
>> That page isn't written as a proposal yet, it's written as a bunch of
>> ideas. I would be happy to see something along the lines of Bas van
>> Dijk's work
>> http://permalink.gmane.org/gmane.comp.lang.haskell.libraries/14740 .
>
> This is a proposal with far-reaching consequences, and with several
> alternative designs. I'm not sure I understand all
> the tradeoffs. Some parts of the proposal are orthogonal to the rest
> (e.g. changing fmap to map), and should probably be
> considered separately.
>
> Could someone please write a detailed proposal, enumerating all the
> pros and cons, and the rationale for this design
> compared to other designs?
>
> Cheers,
> Simon

I don't know exactly what the proposal process is, but what I'd like to
see is something like the following:

1)	"Subclasses" may declare default implementations of inherited methods. 
For example:

> 	class Functor f => Applicative f where
> 		...
>		fmap f x = pure f <*> x
>
>	class Applicative f => Monad f where
>		...
>		pure = return
>		(<*>) = ap

2)	Unless superclass instances are already in scope at declaration of an
instance,  an instance declaration implicitly also declares the superclass
(and may also explicitly define functions in the superclass).  For
example:

>	instance Monad Maybe where
>		return = Just
>		(>>=) = ...
>		fmap = ...

This declaration, in the absence of any explicit "instance Functor Maybe"
and "instance Applicative Maybe", would implicitly define those instances
as well, with the default implementations of fmap, pure, and <*> given.

It would be a compile-time error to inherit multiple default definitions
of a method, unless:
	a) There is a clear "shadowing" (eg, if the Monad class declaration
included a default fmap, that would take precedence over the one in
Applicative)
	b) The instance declaration explicitly defines the function, thus
resolving the conflict.

These changes, I believe, would make it possible to restructure the
heirarchy with negligible impact on user code.  The only potential impact
I see so far would have to involve orphan instances, which are already
considered risky/not a good idea.  Specifically, if there were already an
orphan Monad instance in one place and an orphan Applicative instance in
another, the orphaned Applicative instance would become a duplicate
instance which could potentially bite an end-user importing both modules.

It would also be possible with fairly small user impact to move 'return'
to Applicative, or even to a new 'Pointed' superclass of Applicative.  To
the end user, the type 'return :: Monad m => a -> m a' would still be
valid, as would including "return" in a Monad instance declaration. 
Including 'pure' as well in Applicative (with defaults pure = return,
return = pure) would allow old Applicative declarations to continue to
work unchanged as well,  though that obviously has the downside of
introducing a new recursive default which is always potentially confusing
to writers of new instances.

	-- James

PS. Incidentally, I'd also prefer a class like the following instead of
Applicative as it is now:
>	class Functor f => Monoidal f where
>		return :: a -> f a
>		(<*>)  :: f a -> f b -> f (a,b)

But that would be a much more disruptive change.





More information about the Haskell-Cafe mailing list