[Haskell-cafe] Retrospective type-class extension

Holger Siegel holgersiegel74 at yahoo.de
Thu May 20 09:55:08 EDT 2010


Am 20.05.2010 um 14:16 schrieb Tony Morris:

> I've compared and clearly the former is significantly superior :)
> 
> I'm rather interested if there are any sound suggestions to resolve the
> general issue of retrospective type-class extension.
> 

I would like to have something like

parent class Functor f <= Applicative f where
  fmap f x = pure f <*> x

Then one could write

instance Applicative MyApplicative deriving parent Functor where
  (<*>) = ...
  pure = ...

as an abbreviation for

instance Functor MyApplicative where
  fmap f x = pure f <*> x

This way, we do not only save some keystrokes, but now it is clear
that (fmap f x == pure f <*> x) is expected to hold for type
MyApplicative. One could also write

parent class Applicative a <= Monad a deriving parent Functor where
  (<*>) = ap
  pure = return
  fmap = liftM

overriding the default definition of Functor's fmap. Then

instance Monad MyMonad deriving parent Applicative where
  (>>=) = ...
  return = ...

would be an abbreviation for

instance Functor MyMonad where
  fmap = liftM

instance Applicative MyMonad where
  (<*>) = ap
  pure = return

Now the compiler can even conclude that (liftM f x == pure f <*> x) is expected to
hold for type MyMonad.

But there is an ambiguity if one also defines

parent class Functor f <= Monad f where
  fmap f x = trace "boo!" (liftM f x)

Then it might not be clear which definition of fmap should be used, because there are two
possible paths: (Monad => Applicative => Functor) and (Monad => Functor). But then the
programmer has to decide whether he writes 'deriving parent Applicative' or 'deriving parent
Functor'. Thus, as long as every class or instance declaration contains at most one 'deriving
parent' statement, there will always be one unambiguous path, so that this will not become
a problem.

This extension would have three advantages:
- it is merely syntactic sugar, so that it can easily be implemented,
- it does not involve tricky resolution of methods or types, so that it is easy to comprehend, and
- it allows to encode knowledge about the laws class instances (should) follow.


> 
> Miguel Mitrofanov wrote:
>> That won't be a great idea; if I just want my monad to be declared as
>> one, I would have to write
>> 
>> instance Functor MyMonad where fmap = ...
>> instance Pointed MyMonad where pure = ...
>> instance Applicative MyMonad where (<*>) = ...
>> instance Monad MyMonad where join = ...
>> 
>> Compare this with
>> 
>> instance Monad MyMonad where
>>  return = ...
>>  (>>=) = ...
>> 
>> and take into account that (>>=) is usually easier to write than join.
>> 
>> Limestraël wrote:
>>> Then it would be:
>>> 
>>> class Functor f where
>>>    fmap :: (a -> b) -> f a -> f b
>>> 
>>> class (Functor f) => Pointed f where
>>>    pure :: a -> f a
>>> 
>>> class (Pointed f) => Applicative f where
>>>    (<*>) :: f (a -> b) -> f a -> f b
>>> 
>>> class (Applicative f) => Monad f where
>>>    join :: f (f a) -> f a
>>> 
>>> This would be a great idea, for the sake of logic, first (a monad
>>> which is not a functor doesn't make sense), and also to eliminate
>>> redudancy (fmap = liftM, ap = (<*>), etc.)
>>> 
>>> 2010/5/20 Tony Morris <tonymorris at gmail.com
>>> <mailto:tonymorris at gmail.com>>
>>> 
>>>    Ivan Miljenovic wrote:
>>>> On 20 May 2010 14:42, Tony Morris <tonymorris at gmail.com
>>>    <mailto:tonymorris at gmail.com>> wrote:
>>>> 
>>>>> We all know that "class (Functor f) => Monad f" is preferable
>>>    but its
>>>>> absence is a historical mistake. We've all probably tried once:
>>>>> 
>>>>> instance (Functor f) => Monad f where
>>>>> 
>>>> 
>>>> Do you mean the reverse of this (instance (Monad m) => Functor m
>>>    where) ?
>>>> 
>>>    Yes.
>>> 
>>>    --
>>>    Tony Morris
>>>    http://tmorris.net/



More information about the Haskell-Cafe mailing list