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.
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
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,
there are several options for disambiguating updates:
Check for types that have all the fields being updated. For example:
f x = x { foo = 3, bar = 2 }
Here
f
must be updatingT
because neitherS
norU
have both fields.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.