large-records-0.4: src/Data/Record/Overloading.hs
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
-- Necessary for correct type for @setField@
{-# LANGUAGE AllowAmbiguousTypes #-}
-- | Provide @Prelude@-like environment when using @RebindableSyntax@
--
-- If you want to use @large-records@ with 'OverloadedRecordDot' and
-- 'OverloadedRecordUpdate', you need to enable the @RebindableSyntax@ language
-- extension. You can then import this module to get a standard environment
-- back, with one exception; see 'getField' for details.
module Data.Record.Overloading (
module Prelude
, Control.Arrow.app
, Control.Arrow.arr
, Control.Arrow.first
, Control.Arrow.loop
, (Control.Arrow.>>>)
, (Control.Arrow.|||)
, Data.String.fromString
, GHC.OverloadedLabels.fromLabel
-- * New definitions
, ifThenElse
, getField
, setField
) where
import qualified Control.Arrow
import qualified Data.String
import qualified GHC.OverloadedLabels
import qualified GHC.Records.Compat
{-------------------------------------------------------------------------------
Other exports
-------------------------------------------------------------------------------}
ifThenElse :: Bool -> a -> a -> a
ifThenElse b x y = if b then x else y
-- | Get record field
--
-- This is /NOT/ the standard @getField@ from "GHC.Records", but is instead
-- defined in terms of "GHC.Records.Compat.HasField". As a consequence we do not
-- currently support @OverloadedRecordDot@ without @RebindableSyntax@.
-- (For ghc 9.2 and 9.4 @RebindableSyntax@ is required /anyway/ when using
-- @OverloadedRecordUpdate@, so this is less of a limitation than it might
-- seem.)
--
-- Unfortunately, we /cannot/ currently support 'GHC.Records.HasField'; in the
-- remainder of this comment we explain why. Consider a record such as
--
-- > {-# ANN type Person largeRecord #-}
-- > data Person = Person { name :: String }
--
-- The internal representation of this record generated by @large-records@
-- looks something like this:
--
-- > data Person = forall a. a ~ String => Person { name :: a }
--
-- This representation prevents ghc from generating record accessors, whilst
-- at the same time still making it possible to define and pattern match on
-- records in the normal way. Unfortunately, although GHC does not generate
-- a 'HasField' instance for such a record, it /also/ prevents us from
-- defining our own:
--
-- 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.
--
-- <https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/hasfield.html#solving-hasfield-constraints>
--
-- There is a proposal to change this
-- (<https://github.com/ghc-proposals/ghc-proposals/pull/515>),
-- but for now we instead rely on 'GHC.Records.Compat.HasField', for which no
-- such restrictions exist.
getField :: forall x r a. GHC.Records.Compat.HasField x r a => r -> a
getField = snd . GHC.Records.Compat.hasField @x
setField :: forall x r a. GHC.Records.Compat.HasField x r a => r -> a -> r
setField = fst . GHC.Records.Compat.hasField @x