.. _record-field-selector-polymorphism:

Record field selector polymorphism
----------------------------------

The module :base-ref:`GHC.Records.` defines the following: ::

  class HasField (x :: k) r a | x r -> a where
    getField :: r -> a

A ``HasField x r a`` constraint represents the fact that ``x`` is a
field of type ``a`` belonging to a record type ``r``.  The
``getField`` method gives the record selector function.

This allows definitions that are polymorphic over record types with a specified
field.  For example, the following works with any record type that has a field
``name :: String``: ::

  foo :: HasField "name" r String => r -> String
  foo r = reverse (getField @"name" r)

``HasField`` is a magic built-in typeclass (similar to ``Coercible``, for
example).  It is given special treatment by the constraint solver (see
:ref:`solving-hasfield-constraints`).  Users may define their own instances of
``HasField`` also (see :ref:`virtual-record-fields`).

.. _solving-hasfield-constraints:

Solving HasField constraints
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If the constraint solver encounters a constraint ``HasField x r a``
where ``r`` is a concrete datatype with a field ``x`` in scope, it
will automatically solve the constraint using the field selector as
the dictionary, unifying ``a`` with the type of the field if
necessary.  This happens irrespective of which extensions are enabled.

For example, if the following datatype is in scope ::

  data Person = Person { name :: String }

the end result is rather like having an instance ::

  instance HasField "name" Person String where
    getField = name

except that this instance is not actually generated anywhere, rather
the constraint is solved directly by the constraint solver.

A field must be in scope for the corresponding ``HasField`` constraint
to be solved.  This retains the existing representation hiding
mechanism, whereby a module may choose not to export a field,
preventing client modules from accessing or updating it directly.

Solving ``HasField`` constraints depends on the field selector functions that
are generated for each datatype definition:

-  If a record field does not have a selector function because its type would allow
   an existential variable to escape, the corresponding ``HasField`` constraint
   will not be solved.  For example, ::

     {-# LANGUAGE ExistentialQuantification #-}
     data Exists t = forall x . MkExists { unExists :: t x }

   does not give rise to a selector ``unExists :: Exists t -> t x`` and we will not
   solve ``HasField "unExists" (Exists t) a`` automatically.

-  If a record field has a polymorphic type (and hence the selector function is
   higher-rank), the corresponding ``HasField`` constraint will not be solved,
   because doing so would violate the functional dependency on ``HasField`` and/or
   require impredicativity.  For example, ::

     {-# LANGUAGE RankNTypes #-}
     data Higher = MkHigher { unHigher :: forall t . t -> t }

   gives rise to a selector ``unHigher :: Higher -> (forall t . t -> t)`` but does
   not lead to solution of the constraint ``HasField "unHigher" Higher a``.

-  A record GADT may have a restricted type for a selector function, which may lead
   to additional unification when solving ``HasField`` constraints.  For example, ::

     {-# LANGUAGE GADTs #-}
     data Gadt t where
       MkGadt :: { unGadt :: Maybe v } -> Gadt [v]

   gives rise to a selector ``unGadt :: Gadt [v] -> Maybe v``, so the solver will reduce
   the constraint ``HasField "unGadt" (Gadt t) b`` by unifying ``t ~ [v]`` and
   ``b ~ Maybe v`` for some fresh metavariable ``v``, rather as if we had an instance ::

     instance (t ~ [v], b ~ Maybe v) => HasField "unGadt" (Gadt t) b

-  If a record type has an old-fashioned datatype context, the ``HasField``
   constraint will be reduced to solving the constraints from the context.
   For example, ::

     {-# LANGUAGE DatatypeContexts #-}
     data Eq a => Silly a = MkSilly { unSilly :: a }

   gives rise to a selector ``unSilly :: Eq a => Silly a -> a``, so
   the solver will reduce the constraint ``HasField "unSilly" (Silly a) b`` to
   ``Eq a`` (and unify ``a`` with ``b``), rather as if we had an instance ::

     instance (Eq a, a ~ b) => HasField "unSilly" (Silly a) b

See :ref:`overloaded-record-dot` for an application of solving ``HasField`` constraints to implementing "record dot syntax".

.. _virtual-record-fields:

Virtual record fields
~~~~~~~~~~~~~~~~~~~~~

Users may define their own instances of ``HasField``, provided they do
not conflict with the built-in constraint solving behaviour.  This
allows "virtual" record fields to be defined for datatypes that do not
otherwise have them.

For example, this instance would make the ``name`` field of ``Person``
accessible using ``#fullname`` as well: ::

  instance HasField "fullname" Person String where
    getField = name

More substantially, an anonymous records library could provide
``HasField`` instances for its anonymous records, and thus be
compatible with the polymorphic record selectors introduced by this
proposal.  For example, something like this makes it possible to use
``getField`` to access ``Record`` values with the appropriate
string in the type-level list of fields: ::

  data Record (xs :: [(k, Type)]) where
    Nil  :: Record '[]
    Cons :: Proxy x -> a -> Record xs -> Record ('(x, a) ': xs)

  instance {-# OVERLAPPING #-} HasField x (Record ('(x, a) ': xs)) a where
    getField (Cons _ v _) = v
  instance HasField x (Record xs) a => HasField x (Record ('(y, b) ': xs)) a where
    getField (Cons _ _ r) = getField @x r

  r :: Record '[ '("name", String) ]
  r = Cons Proxy "R" Nil

  x = getField @"name" r

Since representations such as this can support field labels with kinds other
than ``Symbol``, the ``HasField`` class is poly-kinded (even though the built-in
constraint solving works only at kind ``Symbol``).  In particular, this allows
users to declare scoped field labels such as in the following example: ::

  data PersonFields = Name

  s :: Record '[ '(Name, String) ]
  s = Cons Proxy "S" Nil

  y = getField @Name s

In order to avoid conflicting with the built-in constraint solving,
the following user-defined ``HasField`` instances are prohibited (in
addition to the usual rules, such as the prohibition on type
families appearing in instance heads):

-  ``HasField _ r _`` where ``r`` is a variable;

-  ``HasField _ (T ...) _`` if ``T`` is a data family (because it
   might have fields introduced later, using data instance declarations);

-  ``HasField x (T ...) _`` if ``x`` is a variable and ``T`` has any
   fields at all (but this instance is permitted if ``T`` has no fields);

-  ``HasField "foo" (T ...) _`` if ``T`` has a field ``foo`` (but this
   instance is permitted if it does not).

If a field has a higher-rank or existential type, the corresponding ``HasField``
constraint will not be solved automatically (as described above), but in the
interests of simplicity we do not permit users to define their own instances
either.  If a field is not in scope, the corresponding instance is still
prohibited, to avoid conflicts in downstream modules.
