Note [DerivEnv and DerivSpecMechanism]

GHC/Tc/Deriv/Utils.hs:363 compiler 1 ticket

DerivEnv contains all of the bits and pieces that are common to every
deriving strategy. (See Note [Deriving strategies] in GHC.Tc.Deriv.) Some deriving
strategies impose stricter requirements on the types involved in the derived
instance than others, and these differences are factored out into the
DerivSpecMechanism type. Suppose that the derived instance looks like this:

  instance ... => C arg_1 ... arg_n

Each deriving strategy imposes restrictions on arg_1 through arg_n as follows:

* stock (DerivSpecStock):

  Stock deriving requires that:

  - n must be a positive number. This is checked by
    GHC.Tc.Deriv.expectNonNullaryClsArgs
  - arg_n must be an application of an algebraic type constructor. Here,
    "algebraic type constructor" means:

    + An ordinary data type constructor, or
    + A data family type constructor such that the arguments it is applied to
      give rise to a data family instance.

    This is checked by GHC.Tc.Deriv.expectAlgTyConApp.

  This extra structure is witnessed by the DerivInstTys data type, which stores
  arg_1 through arg_(n-1) (dit_cls_tys), the algebraic type constructor
  (dit_tc), and its arguments (dit_tc_args). A DerivInstTys value can be seen
  as a more structured representation of the denv_inst_tys field of DerivEnv.

  If dit_tc is an ordinary data type constructor, then
  dit_rep_tc/dit_rep_tc_args are the same as dit_tc/dit_tc_args. If dit_tc is a
  data family type constructor, then dit_rep_tc is the representation type
  constructor for the data family instance, and dit_rep_tc_args are the
  arguments to the representation type constructor in the corresponding
  instance.

* newtype (DerivSpecNewtype):

  Newtype deriving imposes the same DerivInstTys requirements as stock
  deriving. This is necessary because we need to know what the underlying type
  that the newtype wraps is, and this information can only be learned by
  knowing dit_rep_tc.

* anyclass (DerivSpecAnyclass):

  DeriveAnyClass is the most permissive deriving strategy of all, as it
  essentially imposes no requirements on the derived instance. This is because
  DeriveAnyClass simply derives an empty instance, so it does not need any
  particular knowledge about the types involved. It can do several things
  that stock/newtype deriving cannot do (#13154):

  - n can be 0. That is, one is allowed to anyclass-derive an instance with
    no arguments to the class, such as in this example:

      class C
      deriving anyclass instance C

  - One can derive an instance for a type that is not headed by a type
    constructor, such as in the following example:

      class C (n :: Nat)
      deriving instance C 0
      deriving instance C 1
      ...

  - One can derive an instance for a data family with no data family instances,
    such as in the following example:

      data family Foo a
      class C a
      deriving anyclass instance C (Foo a)

* via (DerivSpecVia):

  Like newtype deriving, DerivingVia requires that n must be a positive number.
  This is because when one derives something like this:

    deriving via Foo instance C Bar

  Then the generated code must specifically mention Bar. However, in
  contrast with newtype deriving, DerivingVia does *not* require Bar to be
  an application of an algebraic type constructor. This is because the
  generated code simply defers to invoking `coerce`, which does not need to
  know anything in particular about Bar (besides that it is representationally
  equal to Foo). This allows DerivingVia to do some things that are not
  possible with newtype deriving, such as deriving instances for data families
  without data instances (#13154):

    data family Foo a
    newtype ByBar a = ByBar a
    class Baz a where ...
    instance Baz (ByBar a) where ...
    deriving via ByBar (Foo a) instance Baz (Foo a)

References 1

Referenced by 9