Proposal: Applicative => Monad: Call for consensus

Conor McBride conor at strictlypositive.org
Wed Jan 5 19:00:51 CET 2011


Hi Dave

On 5 Jan 2011, at 17:25, David Menendez wrote:

> On Wed, Jan 5, 2011 at 8:05 AM, Conor McBride
> <conor at strictlypositive.org> wrote:
>>
>> On 5 Jan 2011, at 12:31, Bas van Dijk wrote:
>>
>>> On Wed, Jan 5, 2011 at 1:14 PM, Conor McBride
>>> <conor at strictlypositive.org> wrote:
>>>>
>>>> cabal install she
>>>
>>> I knew about her but not that she supported default superclass  
>>> instances.
>>> Nice!
>>
>> She didn't. But she does now.
>
> How does she handle multiple subclasses?
>
> E.g., Functor can be defined in terms of Applicative (fmap = liftA)
> and in terms of Traversable (fmap = liftT). There are plenty of types
> which are both.

She lets you opt out. This is vital to any such default subclass
scheme.

I can write

instance Monad Maybe where
   return = Just
   Nothing >>= _ = Nothing
   Just x >>= f = f x
   hiding instance Functor Maybe  -- notation renegotiable

and I'll get the Monad and Applicative instances, but not the Functor.
Of course, if I don't otherwise provide a Functor instance, GHC will
complain that it's missing. Crucially, however, I'm not forced to take
the default.

The ability to opt out of the default is crucial for several reasons:

   (1) You might have a choice of defaults, e.g. indeed, Functor from
         Applicative or Traversable, and you must choose at most one,
         even if they are extensionally equal.

   (2) You might have inherited a Functor from a library whose author
         did not know or care that it was Applicative (because it was
         obviously not a Monad, of course). You should not be prevented
         from declaring an Applicative instance by the prior Functor
         instance.

   (3) In the case of monad transformers, I might not want an instance
         Monad m => Functor (T m), especially if it prevents me from
         defining an instance Functor f => Functor (T f). (cf Oleg's
         example.)

Please note that it's the cheapness of opting in, not requiring the
user to assign the methods to their superclasses which assists with
backward compatibility. It's been pointed out to me that one might
consider

   class (A x, A y) => B x y where
     bthingy = ..
     instance A x where
       aring = ..
       ading = ..
     instance A y where
       aring = ..
       ading = ..


with *two* default A instances. If we were to allow this, we should
need to be able to switch at least one off in some

   instance B x x

and, otherwise, to write stuff like

   instance B Int Bool where
     bthingy = ..
     instance A Int where
       aring = ..

to be clear which A instance gets the overridden aring. At the
moment, she doesn't handle this terribly well: you can have both
defaults (unmodified) or neither.

It's been helpful thinking about the problem in preprocessor terms,
because it helps to figure out where the controls need to be.

I hope this is progress, anyway.

Cheers

Conor




More information about the Libraries mailing list