6.6.6. Deriving any other class¶
-
DeriveAnyClass
¶ Since: 7.10.1 Allow use of any typeclass in
deriving
clauses.
With DeriveAnyClass
you can derive any other class. The compiler
will simply generate an instance declaration with no explicitly-defined
methods.
This is
mostly useful in classes whose minimal set is
empty, and especially when writing
generic functions.
As an example, consider a simple pretty-printer class SPretty
, which outputs
pretty strings:
{-# LANGUAGE DefaultSignatures, DeriveAnyClass #-}
class SPretty a where
sPpr :: a -> String
default sPpr :: Show a => a -> String
sPpr = show
If a user does not provide a manual implementation for sPpr
, then it will
default to show
. Now we can leverage the DeriveAnyClass
extension to
easily implement a SPretty
instance for a new data type:
data Foo = Foo deriving (Show, SPretty)
The above code is equivalent to:
data Foo = Foo deriving Show
instance SPretty Foo
That is, an SPretty Foo
instance will be created with empty implementations
for all methods. Since we are using DefaultSignatures
in this example, a
default implementation of sPpr
is filled in automatically.
Note the following details
In case you try to derive some class on a newtype, and
GeneralizedNewtypeDeriving
is also on,DeriveAnyClass
takes precedence.The instance context is determined by the type signatures of the derived class’s methods. For instance, if the class is:
class Foo a where bar :: a -> String default bar :: Show a => a -> String bar = show baz :: a -> a -> Bool default baz :: Ord a => a -> a -> Bool baz x y = compare x y == EQ
And you attempt to derive it using
DeriveAnyClass
:instance Eq a => Eq (Option a) where ... instance Ord a => Ord (Option a) where ... instance Show a => Show (Option a) where ... data Option a = None | Some a deriving Foo
Then the derived
Foo
instance will be:instance (Show a, Ord a) => Foo (Option a)
Since the default type signatures for
bar
andbaz
requireShow a
andOrd a
constraints, respectively.Constraints on the non-default type signatures can play a role in inferring the instance context as well. For example, if you have this class:
class HigherEq f where (==#) :: f a -> f a -> Bool default (==#) :: Eq (f a) => f a -> f a -> Bool x ==# y = (x == y)
And you tried to derive an instance for it:
instance Eq a => Eq (Option a) where ... data Option a = None | Some a deriving HigherEq
Then it will fail with an error to the effect of:
No instance for (Eq a) arising from the 'deriving' clause of a data type declaration
That is because we require an
Eq (Option a)
instance from the default type signature for(==#)
, which in turn requires anEq a
instance, which we don’t have in scope. But if you tweak the definition ofHigherEq
slightly:class HigherEq f where (==#) :: Eq a => f a -> f a -> Bool default (==#) :: Eq (f a) => f a -> f a -> Bool x ==# y = (x == y)
Then it becomes possible to derive a
HigherEq Option
instance. Note that the only difference is that now the non-default type signature for(==#)
brings in anEq a
constraint. Constraints from non-default type signatures never appear in the derived instance context itself, but they can be used to discharge obligations that are demanded by the default type signatures. In the example above, the default type signature demanded anEq a
instance, and the non-default signature was able to satisfy that request, so the derived instance is simply:instance HigherEq Option
DeriveAnyClass
can be used with partially applied classes, such asdata T a = MKT a deriving( D Int )
which generates
instance D Int a => D Int (T a) where {}
DeriveAnyClass
can be used to fill in default instances for associated type families:{-# LANGUAGE DeriveAnyClass, TypeFamilies #-} class Sizable a where type Size a type Size a = Int data Bar = Bar deriving Sizable doubleBarSize :: Size Bar -> Size Bar doubleBarSize s = 2*s
The
deriving( Sizable )
is equivalent to sayinginstance Sizeable Bar where {}
and then the normal rules for filling in associated types from the default will apply, making
Size Bar
equal toInt
.