Note [Unary class magic]

GHC/Core/TyCon.hs:1437 compiler

Consider a class with just one method, or with no methods and one
superclass:
  class UC a where { op :: a -> a }
  class Eq a => UD a where {}
Such a class is called a /unary class/.

We could represent the dictionary for a unary class with a data type:
  data UC a where { MkUC :: (a->a) -> UC a }
  data UD a where { MkUD :: Eq a =>  UD a }
But it would be more efficent to use a newtype; and for decades GHC did
exactly that, because:

  * Unary classes are surprisingly common, so it's a useful optimisation.

  * The `reflection` library uses `unsafeCoerce` to /rely/ on the fact that
    a unary class is ultimately represented by its payload.  We may not like
    it, and I hope to ultimately eliminate the necessity for this by using
    `withDict` (see Note [withDict] in GHC.Tc.Instance.Class).  But meanwhile
    we'd prefer not to break this usage.

But alas, using a newtype representation (surprisingly) led multiple, subtle,
Bad Things: see Note [Representing unary classes with newtypes: bad, bad, bad].

This Note explains what GHC now does for unary classes.

(UCM0) Throughout the compiler, right up to the code generator, GHC thinks that a
  unary class is just like a non-unary class:
    - Represented by a data type,
    - with one constructor,
    - which has one field

(UCM1) Then when converting from Core to STG, in GHC.CoreToStg, we effectively
  transform
    - op   ta tb tc dict_arg  -->  dict_arg
    - MkUC ta tb tc meth_arg  -->  meth_arg

  Note that we do this transformation well /after/ generating an interface file,
  so importing modules only see the data constructor.

  This late transformation has a lot in common with the treatment of
  `unsafeEqualityProof`; see (U2) in Note [Implementing unsafeCoerce]
  in GHC.Internal.Unsafe.Coerce.

In this way we get the efficiency of a newtype without the bugs that we get
by exposing the newtype representation too early.

There are a number of wrinkles

(UCM2) The TyCon for a unary class is /not/ identified as a newtype.
   Rather, it has its own AlgTyConRhs, namely `UnaryClassTyCon`

(UCM3) Unlike non-unary classes, a value of type (C ty), where `C` is a unary
   class, might be bottom, because it is represented by the method type alone.
   See GHC.Core.Type.isTerminatingType.

   Similarly in exprOkForSpeculation/exprOkToDiscard/exprOkForSpecEval,
   in GHC.Core.Utils.  In the utility funcion `app_ok` we need a special
   case for the DFunIds; they generally terminate, but not for unary classes.

(UMC4) To avoid regressions, in Core we want to remember that
             (MkUC x) is really just  x
             (op d)   is really just  d
    We account for this in several places:

    - `GHC.Core.Utils.exprIsTrivial` treats the above two forms as trivial

    - `GHC.Core.Unfold.sizeExpr` (which computes the size of an expression to
      guide inlining) treats (MkUC e) as the same size as `e`, and similarly
      (op d).

    - `GHC.Core.Unfold.inlineBoringOK` where we want to ensure that we
      always-inline (MkUC op), even into a boring context. See (IB6)
      in Note [inlineBoringOk]

(UCM5) `GHC.Core.Unfold.Make.mkDFunUnfolding` builds a `DFunUnfolding` for
   non-unary classes, but just an /ordinary/ unfolding for unary classes.
       instance Num a => Num [a] where { .. }       -- (I1)
       instance UC a => UC [a] where { op = $cop }  -- (I2)
   From (I1) we get
       $fNumList = /\a \(d:Num a). MkNum (..) (..) (..)
         $fNumList has a DFunUnfolding
    But from (I2) we get
       $fUCList = /\a (d:UC a). MkUC ($cop a d)
       $fUCList has a regular CoreUnfolding

    Why?  Because we can safely inline $fUCList without code-size blow-up.
    Just one less indirection. It'd probably work ok with a DFunUnfolding;
    and it'd add another case for (UCM4) to spot.

(UCM6) In the constraint solver, when constructing evidence for a unary class
    (e.g. implicit parameters, withDict) be careful to use
    - the data constructor to build it: see `evDictApp`, `evUnaryDictAppE`
    - the class op to take it apart: see `evUnwrapIP`

(UCM7) You might worry about
           class UC1 a where { op :: Int# }    -- Single unboxed field
           class (a ~# b) => UC2 a b where {}  -- Unboxed equality superclass
  But these are illegal: predicates are always boxed, and all classes must have
  lifted fields.

(UCM8) The data constructor for a unary class has no wrapper, just a worker.
  (And the worker is turned into a cast by GHC.CoreToStg.Prep.isUnaryClassApp,
  as described above.)

(UCM9) Unary classes are treated as injective by `isInjectiveTyCon`, just like
  non-unary classes (which are TupleTyCons or DataTyCons).  This matters,
  because of the injectivity check done by lintCoercion (SelCo cs co)
  in GHC.Core.Lint.  There is a similar injectivity check in
  GHC.Core.Opt.Arity.pushCoDataCon.

  Generally, we want unary classes to behave like ordinary non-unary ones.

(UCM10) When, precisely, is a class unary?  It is unary iff
                  it has one field (superclass or method)
                  of boxed type
  The boxed-ness important. Consider
          class (a ~# b) => a ~ b where {}
  which is `eqClass` in GHC.Builtin.Types.  This has only one field, but it is
  definitely not a unary class: it is definitely represented by an ordinary
  algebraic data type with a single field of type (a ~# b).

  See `unary_class` in `GHC.Tc.TyCl.tcClassDecl1`

(UCM11) When building evidence for classes (unary or not) and implicit parameters,
  the constraint solver is careful to use functions that hide the precise
  evidence construction method.  Eg.g `evWrapIPE`.

(UCM12) In an interface-file description of a Class, we record whether or not
  the class is unary.  In theory this field is redundant, but because its value
  depends on the superclass and method fields, it's very easy to end up with
  a black hole when rehydrating interface the interface file. Easiest just to
  store the bit!  See `ifUnary` in GHC.Iface.Synatax.IfaceClassBody.

References 4

Referenced by 17