Note [DerivEnv and DerivSpecMechanism]
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
- Deriving strategies GHC.Tc.Deriv
Referenced by 9
- GHC.Tc.Deriv call site ×4
- GHC.Tc.Deriv.Utils call site ×4
- GHC.Tc.Deriv.Generate call site