2

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?

Shersh
  • 9,019
  • 3
  • 33
  • 61
theduke
  • 3,027
  • 4
  • 29
  • 28

3 Answers3

2

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 }
Shersh
  • 9,019
  • 3
  • 33
  • 61
2

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:

Linus Arver
  • 1,331
  • 1
  • 13
  • 18
1

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
leftaroundabout
  • 117,950
  • 5
  • 174
  • 319
  • With `-XNamedFieldPuns`, we can also write `combine a B{ name } = a{ name = name }`. – danidiaz Dec 17 '17 at 14:35
  • @danidiaz _can_ we do this? Well, if you say so. IMO we _shouldn't_, at any rate; `{ name = name }` looks utterly confusing. All these extensions are unidiomatic hacks within the old record system that just doesn't work well this way. When `OverloadedRecordFields` will properly work via the `HasField` class and magic-hash syntax, that will be a different pair of shoes, but until then I'd stick to what the lens library has to offer. – leftaroundabout Dec 17 '17 at 14:47