.. _overloaded-record-update:

Overloaded record update
------------------------

.. extension:: OverloadedRecordUpdate
    :shortdesc: Record '.' syntax record updates

    :since: 9.2.0

    Provides record '.' syntax in record updates e.g. ``x{foo.bar = 1}``.

**EXPERIMENTAL**
*This design of this extension may well change in the future. It would be inadvisable to start using this extension for long-lived libraries just yet.*

It's usual (but not required) that this extension be used in conjunction with :ref:`overloaded-record-dot`.

Example:

.. code-block:: haskell

  {-# LANGUAGE AllowAmbiguousTypes, FunctionalDependencies, ScopedTypeVariables, PolyKinds, TypeApplications, DataKinds, FlexibleInstances #-}
  {-# LANGUAGE NamedFieldPuns, RecordWildCards #-}
  {-# LANGUAGE OverloadedRecordDot, OverloadedRecordUpdate, RebindableSyntax #-}

  import Prelude

  class HasField x r a | x r -> a where
    hasField :: r -> (a -> r, a)

  getField :: forall x r a . HasField x r a => r -> a
  getField = snd . hasField @x -- Note: a.x = is getField @"x" a.
  setField :: forall x r a . HasField x r a => r -> a -> r
  setField = fst . hasField @x -- Note : a{x = b} is setField @"x" a b.

  data Person = Person { name :: String } deriving Show
  instance HasField "name" Person String where
      hasField r = (\x -> case r of Person { .. } -> Person { name = x, .. }, name r)

  data Company = Company { company :: String, owner :: Person } deriving Show
  instance HasField "company" Company String where
      hasField r = (\x -> case r of Company { .. } -> Company { company = x, .. }, company r)
  instance HasField "owner" Company Person where
      hasField r = (\x -> case r of Company { .. } -> Company { owner = x, .. }, owner r)

  main = do
    let c = Company {company = "Acme Corp.", owner = Person { name = "Wile E. Coyote" }}

    -- Top-level update
    print $ c{company = "Acme United"} -- Company {company = "Acme United", owner = Person {name = "Wile E. Coyote"}}

    -- Nested update
    print $ c{owner.name = "Walter C. Johnsen"} -- Company {company = "Acme Corp.", owner = Person {name = "Walter C. Johnsen"}}

    -- Punned update
    let name = "Walter C. Johnsen"
    print $ c{owner.name}  -- Company {company = "Acme Corp.", owner = Person {name = "Walter C. Johnsen"}}

``OverloadedRecordUpdate`` works by desugaring record ``.`` update expressions to expressions involving the functions ``setField`` and ``getField``. Note that all record updates will be desugared to ``setField`` expressions whether they use ``.`` notation or not.

At this time, ``RebindableSyntax`` must be enabled when ``OverloadedRecordUpdate`` is and users are required to provide definitions for ``getField`` and ``setField``. We anticipate this restriction to be lifted in a future release of GHC with builtin support for ``setField``.