Disambiguate record update with DuplicateRecordFields

906 Views Asked by At

I'm using the DuplicateRecordFields (+OverloadedLabels) extension, and I've run into a situation where I can't figure out how to disambiguate in a record update.

Here is a simplified example:

data A = A { name :: String }
data B = B { name :: String }

combine :: A -> B -> A
combine a b = a { name = name b }

Is there any way to make this work?

3

There are 3 best solutions below

0
On

I answered in one of the previous questions about -XDuplicateRecordFields that currently GHC doesn't infer type of record field from its argument:

What you can do now is to specify type of name extractor explicitly, like this:

{-# LANGUAGE DuplicateRecordFields #-}

data A = A { name :: String }
data B = B { name :: String }

combine :: A -> B -> A
combine a b = a { name = (name :: B -> String) b }
2
On

You could match the name from a pattern:

data A = A { name :: String }
data B = B { name :: String }

combine :: A -> B -> A
combine a B{name = nb} = a { name = nb }

I'm not a fan of DuplicateRecordFields though. Why not instead go the lens route?

{-# LANGUAGE TemplateHaskell, FlexibleInstances, FunctionalDependencies #-}

import Control.Lens
import Control.Lens.TH

data A = A { _aName :: String }
makeFields ''A
data B = B { _bName :: String }
makeFields ''B

combine :: A -> B -> A
combine a b = a & name .~ b^.name
0
On

Alternatively, you can mechanically use getField from GHC.Records to disambiguate, like this:

{-# LANGUAGE DataKinds #-}                                                                                                                                                         
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE TypeApplications #-}

module DRF where

import GHC.Records (getField)

data A = A { name :: String } deriving Show
data B = B { name :: String }

combine :: A -> B -> A
combine a b = a { name = getField @"name" b }

{- in ghci

Prelude DRF> a = A "Alice"  
Prelude DRF> b = B "Bob"    
Prelude DRF> DRF.combine a b
A {name = "Bob"}

-}

References: