{-|
Module: Data.Deriving
Copyright: (C) 2015-2016 Ryan Scott
License: BSD-style (see the file LICENSE)
Maintainer: Ryan Scott
Portability: Template Haskell
This module reexports all of the functionality of the other modules in this library.
It also provides a high-level tutorial on @deriving-compat@'s naming conventions and
best practices. Typeclass-specific information can be found in their respective
modules.
-}
module Data.Deriving (
-- * @derive@- functions
-- $derive
-- * @make@- functions
-- $make
module Exports
) where
import Data.Foldable.Deriving as Exports
import Data.Functor.Deriving as Exports
import Data.Traversable.Deriving as Exports
import Text.Show.Deriving as Exports
{- $derive
Functions with the @derive@- prefix can be used to automatically generate an instance
of a typeclass for a given datatype 'Name'. Some examples:
@
{-# LANGUAGE TemplateHaskell #-}
import Data.Deriving
data Pair a = Pair a a
$('deriveFunctor' ''Pair) -- instance Functor Pair where ...
data Product f g a = Product (f a) (g a)
$('deriveFoldable' ''Product)
-- instance (Foldable f, Foldable g) => Foldable (Pair f g) where ...
@
If you are using @template-haskell-2.7.0.0@ or later (i.e., GHC 7.4 or later),
then @derive@-functions can be used with data family instances (which requires the
@-XTypeFamilies@ extension). To do so, pass the 'Name' of a data or newtype instance
constructor (NOT a data family name!) to @deriveFoldable@. Note that the
generated code may require the @-XFlexibleInstances@ extension. Example:
@
{-# LANGUAGE FlexibleInstances, TemplateHaskell, TypeFamilies #-}
import Data.Deriving
class AssocClass a b where
data AssocData a b
instance AssocClass Int b where
data AssocData Int b = AssocDataInt1 Int
| AssocDataInt2 b
$('deriveFunctor' 'AssocDataInt1) -- instance Functor (AssocData Int) where ...
-- Alternatively, one could use $(deriveFunctor 'AssocDataInt2)
@
@derive@-functions in @deriving-compat@ fall into one of three categories:
* Category 0: Typeclasses with an argument of kind @*@.
('deriveEq', 'deriveOrd', 'deriveRead', 'deriveShow')
* Category 1: Typeclasses with an argument of kind @* -> *@, That is, a datatype
with such an instance must have at least one type variable, and the last type
variable must be of kind @*@.
('deriveEq1', 'deriveFoldable', 'deriveFunctor', 'deriveOrd1',
'deriveRead1', 'deriveShow1', 'deriveTraversable')
* Category 2: Typeclasses with an argument of kind @* -> * -> *@. That is, a datatype
with such an instance must have at least two type variables, and the last two type
variables must be of kind @*@.
('deriveEq2', 'deriveOrd2', 'deriveRead2', 'deriveShow2')
Note that there are some limitations to @derive@-functions:
* The 'Name' argument must not be of a type synonym.
* Type variables (other than the last ones) are assumed to require typeclass
constraints. The constraints are different depending on the category. For example,
for Category 0 functions, other type variables of kind @*@ are assumed to be
constrained by that typeclass. As an example:
@
data Foo a = Foo a
$(deriveEq ''Foo)
@
will result in a generated instance of:
@
instance Eq a => Eq (Foo a) where ...
@
If you do not want this behavior, use a @make@- function instead.
* For Category 1 and 2 functions, if you are using the @-XDatatypeContexts@ extension,
a constraint cannot mention the last type variables. For example,
@data Illegal a where I :: Ord a => a -> Illegal a@ cannot have a derived 'Functor'
instance.
* For Category 1 and 2 functions, if one of the last type variables is used within a
constructor field's type, it must only be used in the last type arguments. For
example, @data Legal a = Legal (Either Int a)@ can have a derived 'Functor' instance,
but @data Illegal a = Illegal (Either a Int)@ cannot.
* For Category 1 and 2 functions, data family instances must be able to eta-reduce the
last type variables. In other words, if you have a instance of the form:
@
data family Family a1 ... an t1 ... tn
data instance Family e1 ... e2 v1 ... vn = ...
@
where @t1@, ..., @tn@ are the last type variables, then the following conditions
must hold:
1. @v1@, ..., @vn@ must be type variables.
2. @v1@, ..., @vn@ must not be mentioned in any of @e1@, ..., @e2@.
-}
{- $make
Functions prefixed with @make@- are similar to @derive@-functions in that they also
generate code, but @make@-functions in particular generate the expression for a
particular typeclass method. For example:
@
{-# LANGUAGE TemplateHaskell #-}
import Data.Deriving
data Pair a = Pair a a
instance Functor Pair where
fmap = $('makeFmap' ''Pair)
@
In this example, 'makeFmap' will splice in the appropriate lambda expression which
implements 'fmap' for @Pair@.
@make@-functions are subject to all the restrictions of @derive@-functions listed
above save for one exception: the datatype need not be an instance of a particular
typeclass. There are some scenarios where this might be preferred over using a
@derive@-function. For example, you might want to map over a @Pair@ value
without explicitly having to make it an instance of 'Functor'.
Another use case for @make@-functions is sophisticated data types—that is, an
expression for which a @derive@-function would infer the wrong instance context.
Consider the following example:
@
data Proxy a = Proxy
$('deriveEq' ''Proxy)
@
This would result in a generated instance of:
@
instance Eq a => Eq (Proxy a) where ...
@
This compiles, but is not what we want, since the @Eq a@ constraint is completely
unnecessary. Another scenario in which @derive@-functions fail is when you
have something like this:
@
newtype HigherKinded f a b = HigherKinded (f a b)
$('deriveFunctor' ''HigherKinded)
@
Ideally, this would produce @HigherKinded (f a)@ as its instance context, but sadly,
the Template Haskell type inference machinery used in @deriving-compat@ is not smart
enough to figure that out. Nevertheless, @make@-functions provide a valuable
backdoor for these sorts of scenarios:
@
{-# LANGUAGE FlexibleContexts, TemplateHaskell #-}
import Data.Foldable.Deriving
data Proxy a = Proxy
newtype HigherKinded f a b = HigherKinded (f a b)
instance Eq (Proxy a) where
(==) = $('makeEq' ''Proxy)
instance Functor (f a) => Functor (HigherKinded f a) where
fmap = $('makeFmap' ''HigherKinded)
@
-}