Superclass defaults

Victor Nazarov asviraspossible at
Tue Aug 30 09:26:54 CEST 2011

I was thinking about the design of superclass default instances. I
think that we can get relatively far using the following extensions

1) Multiple instance declarations

instance (Functor[a], Monad [a])
    fmap = map
    (>>=) = flip concatMap
    return = (:[])

-- Declaration above is syntactic sugar for 2 declarations:
-- instance Functor[a]
--  where
--    fmap = map
-- instance Monad [a]
--  where
--    (>>=) = flip concatMap
--    return = (:[])

2) Context synonyms

-- (MonadAndFunctor a) is synonym for (Functor a, Monad a)
context (Functor a, Monad a) => MonadAndFunctor a

-- Using synonims with multiple class declarations we can define instances like
instance MonadAndFunctor [a]
    fmap = map
    (>>=) = flip concatMap
    return = (:[])

-- Declaration above is syntactic sugar for
-- instance (Functor[a], Monad [a])
--  where
--    fmap = map
--    (>>=) = flip concatMap
--    return = (:[])

3) And finally Default superclass instances

Class contains default instances for superclasses:

class Functor m => Monad m
    (>>=) :: m a -> (a -> m b) -> m b
    return :: a -> m a

    -- default superclass instance:
    instance Functor m
        fmap f m = m >>= (return . f)

Default superclass implementations are used only when multiple
instance declarations are used:

-- no default superclass instance is used. Error is emitted when there
is no Functor instance
instance Monad [a]

-- default superclass instance is used:
instance Functor [a], Monad [a]
    (>>=) = ...
    return = ...

    -- functor instance is generated automatically
    -- fmap = ...

Suppose that we make Functor to be Monad's superclass.
Combination of this three extensions allows us to define compatibility modules:

    module Control.Monad.Compat (Monad) where

    import qualified Control.Monad (Monad(..), Functor(..)) as CM

    context CM.Functor m, CM.Monad m => Monad m

When we have compilation failure in client code after our Monad
definition change: "No Functor instance found for Foo":

instance Monad Foo
  where ...

, we simply add following two lines to the module:

import Prelude hiding (Monad)
import Control.Monad.Compat (Monad)

and compilation succeeds.

Client code can remain Haskell 98/2010 and doesn't require any extensions.
Three extensions seems simple when separate (I think there are many
corner cases)

Intervention is required into client code (But I think it is required anyway).

Victor Nazarov

More information about the Glasgow-haskell-users mailing list