Superclass defaults

Victor Nazarov asviraspossible at gmail.com
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
together:

1) Multiple instance declarations

instance (Functor[a], Monad [a])
  where
    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]
  where
    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
  where
    (>>=) :: m a -> (a -> m b) -> m b
    return :: a -> m a

    -- default superclass instance:
    instance Functor m
      where
        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]
  where
     ...

-- default superclass instance is used:
instance Functor [a], Monad [a]
  where
    (>>=) = ...
    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.

Pros:
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)

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

-- 
Victor Nazarov



More information about the Glasgow-haskell-users mailing list