6.5.4. Duplicate record fields¶
-
DuplicateRecordFields
¶ Implies: DisambiguateRecordFields
Since: 8.0.1 Allow definition of record types with identically-named fields.
Going beyond DisambiguateRecordFields
(see Record field disambiguation),
the DuplicateRecordFields
extension allows multiple datatypes to be
declared using the same field names in a single module. For example, it allows
this:
module M where
data S = MkS { x :: Int }
data T = MkT { x :: Bool }
Uses of fields that are always unambiguous because they mention the constructor,
including construction and pattern-matching, may freely use duplicated field
names. For example, the following are permitted (just as with
DisambiguateRecordFields
):
s = MkS { x = 3 }
f (MkT { x = b }) = b
Field names used as selector functions or in record updates must be unambiguous, either because there is only one such field in scope, or because a type signature is supplied, as described in the following sections.
While DuplicateRecordFields
permits multiple fields with the same
name in a single module, it does not permit a field and a normal value binding
to have the same name. For that, use NoFieldSelectors
.
6.5.4.1. Selector functions¶
Fields may be used as selector functions only if they are unambiguous, so this
is still not allowed if both S(x)
and T(x)
are in scope:
bad r = x r
Warning: the type-based disambiguation rules described in the remainder of
this section are being removed (see the proposal DuplicateRecordFields without
ambiguous field access).
The -Wambiguous-fields
option will warn about code that relies on
these rules. In a future GHC release, such code will produce ambiguity errors.
An ambiguous selector may be disambiguated by the type being “pushed down” to the occurrence of the selector (see Type inference for more details on what “pushed down” means). For example, the following are permitted:
ok1 = x :: S -> Int
ok2 :: S -> Int
ok2 = x
ok3 = k x -- assuming we already have k :: (S -> Int) -> _
In addition, the datatype that is meant may be given as a type signature on the argument to the selector:
ok4 s = x (s :: S)
However, we do not infer the type of the argument to determine the datatype, or have any way of deferring the choice to the constraint solver. Thus the following is ambiguous:
bad :: S -> Int
bad s = x s
Even though a field label is duplicated in its defining module, it may be
possible to use the selector unambiguously elsewhere. For example, another
module could import S(x)
but not T(x)
, and then use x
unambiguously.
6.5.4.2. Record updates¶
In a record update such as e { x = 1 }
, if there are multiple x
fields
in scope, then the type of the context must fix which record datatype is
intended, or a type annotation must be supplied. Consider the following
definitions:
data S = MkS { foo :: Int }
data T = MkT { foo :: Int, bar :: Int }
data U = MkU { bar :: Int, baz :: Int }
Without DuplicateRecordFields
, an update mentioning foo
will always be
ambiguous if all these definitions were in scope. When the extension is enabled,
and there is exactly one type that has all the fields being updated, that type will be used.
For example:
f x = x { foo = 3, bar = 2 }
Here f
must be updating T
because neither S
nor U
have both
fields.
If there are multiple types with all the fields, type information may be used to
disambiguate which record type is meant. Warning: the following rules are
being removed (see the proposal DuplicateRecordFields without ambiguous field
access).
The -Wambiguous-fields
option will warn about code that relies on
these rules. In a future GHC release, such code will produce ambiguity errors.
Use the type being pushed in to the record update, as in the following:
g1 :: T -> T g1 x = x { foo = 3 } g2 x = x { foo = 3 } :: T g3 = k (x { foo = 3 }) -- assuming we already have k :: T -> _
Use an explicit type signature on the record expression, as in:
h x = (x :: T) { foo = 3 }
The type of the expression being updated will not be inferred, and no constraint-solving will be performed, so the following will be rejected as ambiguous:
let x :: T
x = blah
in x { foo = 3 }
\x -> [x { foo = 3 }, blah :: T ]
\ (x :: T) -> x { foo = 3 }
6.5.4.3. Import and export of record fields¶
When DuplicateRecordFields
is enabled, an ambiguous field must be exported
as part of its datatype, rather than at the top level. For example, the
following is legal:
module M (S(x), T(..)) where
data S = MkS { x :: Int }
data T = MkT { x :: Bool }
However, this would not be permitted, because x
is ambiguous:
module M (x) where ...
Similar restrictions apply on import.