summaryrefslogtreecommitdiff
path: root/docs/users_guide/glasgow_exts.rst
diff options
context:
space:
mode:
Diffstat (limited to 'docs/users_guide/glasgow_exts.rst')
-rw-r--r--docs/users_guide/glasgow_exts.rst127
1 files changed, 127 insertions, 0 deletions
diff --git a/docs/users_guide/glasgow_exts.rst b/docs/users_guide/glasgow_exts.rst
index 880967098a..3c340feeda 100644
--- a/docs/users_guide/glasgow_exts.rst
+++ b/docs/users_guide/glasgow_exts.rst
@@ -3963,6 +3963,10 @@ where
missing last argument to ``C`` is not used at a nominal role in any
of the ``C``'s methods. (See :ref:`roles`.)
+- ``C`` is allowed to have associated type families, provided they meet the
+ requirements laid out in the section on :ref:`GND and associated types
+ <gnd-and-associated-types>`.
+
Then the derived instance declaration is of the form ::
instance C t1..tj t => C t1..tj (T v1...vk)
@@ -3998,6 +4002,129 @@ applies (section 4.3.3. of the Haskell Report). (For the standard
classes ``Eq``, ``Ord``, ``Ix``, and ``Bounded`` it is immaterial
whether the stock method is used or the one described here.)
+.. _gnd-and-associated-types:
+
+Associated type families
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+:ghc-flag:`-XGeneralizedNewtypeDeriving` also works for some type classes with
+associated type families. Here is an example: ::
+
+ class HasRing a where
+ type Ring a
+
+ newtype L1Norm a = L1Norm a
+ deriving HasRing
+
+The derived ``HasRing`` instance would look like ::
+
+ instance HasRing a => HasRing (L1Norm a) where
+ type Ring (L1Norm a) = Ring a
+
+To be precise, if the class being derived is of the form ::
+
+ class C c_1 c_2 ... c_m where
+ type T1 t1_1 t1_2 ... t1_n
+ ...
+ type Tk tk_1 tk_2 ... tk_p
+
+and the newtype is of the form ::
+
+ newtype N n_1 n_2 ... n_q = MkN <rep-type>
+
+then you can derive a ``C c_1 c_2 ... c_(m-1)`` instance for
+``N n_1 n_2 ... n_q``, provided that:
+
+- The type parameter ``c_m`` occurs once in each of the type variables of
+ ``T1`` through ``Tk``. Imagine a class where this condition didn't hold.
+ For example: ::
+
+ class Bad a b where
+ type B a
+
+ instance Bad Int a where
+ type B Int = Char
+
+ newtype Foo a = Foo a
+ deriving (Bad Int)
+
+ For the derived ``Bad Int`` instance, GHC would need to generate something
+ like this: ::
+
+ instance Bad Int a => Bad Int (Foo a) where
+ type B Int = B ???
+
+ Now we're stuck, since we have no way to refer to ``a`` on the right-hand
+ side of the ``B`` family instance, so this instance doesn't really make sense
+ in a :ghc-flag:`-XGeneralizedNewtypeDeriving` setting.
+
+- ``C`` does not have any associated data families (only type families). To
+ see why data families are forbidden, imagine the following scenario: ::
+
+ class Ex a where
+ data D a
+
+ instance Ex Int where
+ data D Int = DInt Bool
+
+ newtype Age = MkAge Int deriving Ex
+
+ For the derived ``Ex`` instance, GHC would need to generate something like
+ this: ::
+
+ instance Ex Age where
+ data D Age = ???
+
+ But it is not clear what GHC would fill in for ``???``, as each data family
+ instance must generate fresh data constructors.
+
+If both of these conditions are met, GHC will generate this instance: ::
+
+ instance C c_1 c_2 ... c_(m-1) <rep-type> =>
+ C c_1 c_2 ... c_(m-1) (N n_1 n_2 ... n_q) where
+ type T1 t1_1 t1_2 ... (N n_1 n_2 ... n_q) ... t1_n
+ = T1 t1_1 t1_2 ... <rep-type> ... t1_n
+ ...
+ type Tk tk_1 tk_2 ... (N n_1 n_2 ... n_q) ... tk_p
+ = Tk tk_1 tk_2 ... <rep-type> ... tk_p
+
+Beware that in some cases, you may need to enable the
+:ghc-flag:`-XUndecidableInstances` extension in order to use this feature.
+Here's a pathological case that illustrates why this might happen: ::
+
+ class C a where
+ type T a
+
+ newtype Loop = MkLoop Loop
+ deriving C
+
+This will generate the derived instance: ::
+
+ instance C Loop where
+ type T Loop = T Loop
+
+Here, it is evident that attempting to use the type ``T Loop`` will throw the
+typechecker into an infinite loop, as its definition recurses endlessly. In
+other cases, you might need to enable :ghc-flag:`-XUndecidableInstances` even
+if the generated code won't put the typechecker into a loop. For example: ::
+
+ instance C Int where
+ type C Int = Int
+
+ newtype MyInt = MyInt Int
+ deriving C
+
+This will generate the derived instance: ::
+
+ instance C MyInt where
+ type T MyInt = T Int
+
+Although typechecking ``T MyInt`` will terminate, GHC's termination checker
+isn't sophisticated enough to determine this, so you'll need to enable
+:ghc-flag:`-XUndecidableInstances` in order to use this derived instance. If
+you do go down this route, make sure you can convince yourself that all of
+the type family instances you're deriving will eventually terminate if used!
+
.. _derive-any-class:
Deriving any other class