Ambiguous record update [GHC-02256]

Flag: -Wambiguous-fields
Enabled by default

The DuplicateRecordFields extension lets you use the same field name in multiple records within the same module. This can lead to problems when using field accessors to access a field of a record.

Examples

Ambiguous Record Update

In this example we declare two records BusinessAccount and PersonalAccount which each contain a field balance which stores the current balance. In the function resetPersonalAccount we use this record field to reset the account balance of a personal account to 0. The compiler can successfully detect that the field balance that we mention refers to the field of the record PersonalAccount and not to the field of the record BusinessAccount, but the mechanism that GHC uses for this is brittle and will be deprecated in a future release. For this reason GHC emits a warning.

before/Accounting.hs:9:42: warning: [GHC-02256] [-Wambiguous-fields]
    The record update account
                        {balance = 0} with type PersonalAccount is ambiguous.
    This will not be supported by -XDuplicateRecordFields in future releases of GHC.
  |
9 | resetPersonalAccount account = account { balance = 0 }
  |  

In order to make your code compile without warnings you can put the record declarations in a separate module and use qualified imports and names to make it unambiguous which record field you mean.

Accounting.hs
Before
{-# LANGUAGE DuplicateRecordFields #-}
module Accounting where

data BusinessAccount = MkBusinessAccount { balance :: Int }

data PersonalAccount = MkPersonalAccount { balance :: Int }

resetPersonalAccount :: PersonalAccount -> PersonalAccount
resetPersonalAccount account = account { balance = 0 }
After
module Accounting where

import qualified AccountingRecords as Personal (PersonalAccount(..))
import qualified AccountingRecords as Business (BusinessAccount(..))

resetPersonalAccount :: Personal.PersonalAccount -> Personal.PersonalAccount
resetPersonalAccount account = account { Personal.balance = 0 }
AccountingRecords.hs
Before
After
{-# LANGUAGE DuplicateRecordFields #-}
module AccountingRecords where

data BusinessAccount = MkBusinessAccount { balance :: Int }

data PersonalAccount = MkPersonalAcccount { balance :: Int }