So, DuplicateRecordFields allows us to define multiple records with the same fields:
data X = X { id :: Int, ...}
data Y = Y { id :: Int, ...}
But now, it's not actually possible to use these in a record update without tripping a deprecation warning:
-- Ambiguous record update with parent type constructor 'X'
setXId :: Int -> X -> X
setXId i x = x { id = i }
Best I can tell, there's no real way to avoid this... in this contrived example, I could of course just write something like
setXId :: Int -> X -> X
setXId i X{..} = X{id = i, ..}
But that's just... not using the record update syntax, and gets very unwieldy in places the record update syntax is actually very convenient. The only other option I'm aware is to use module-qualified field names, which then means I have to create a new file for every little datatype I want to introduce? Especially for things like database models, API specs or forms, this is kind of unwieldy.
Otherwise I could of course also just... assign different names to these fields, but that kind of defeats the point of having DuplicateRecordFields.
There's OverloadedRecordUpdate, which I guess "fixes" this, but requires RebindableSyntax (a whole other can of worms) and is still marked as experimental last I checked.
So, am I missing something here? I guess I could use optics/lens, but I extremely rarely have any need for any of the actually "interesting" use cases for these, and they're pretty heavy dependencies just for some basic mucking around with records. So far I've just resorted to `-Wno-ambiguous-fields` because it still works for now, but clearly that's not not the intended long-term strategy.
Full examples
Normal:
{-# LANGUAGE GHC2024, DuplicateRecordFields #-}
newtype X = X { id :: Int }
newtype Y = Y { id :: Int }
-- test.hs:7:18: warning: [GHC-02256] [-Wambiguous-fields]
-- Ambiguous record update with parent type constructor ‘X’.
-- This type-directed disambiguation mechanism will not be supported
-- by -XDuplicateRecordFields in future releases of GHC.
-- Consider disambiguating using module qualification instead.
setXId :: Int -> X -> X
setXId i x = x { id = i }
The version with OverloadedRecordDot (works I guess but I don't really want to turn on RebindableSyntax everywhere)
{-# LANGUAGE
GHC2024
, DuplicateRecordFields
, OverloadedRecordUpdate
, RebindableSyntax
, AllowAmbiguousTypes
#-}
import GHC.TypeLits(KnownSymbol)
import Prelude(Int)
newtype X = X { id :: Int }
newtype Y = Y { id :: Int }
setXId :: Int -> X -> X
setXId i x = x { id = i }
getField :: forall f. KnownSymbol f => X -> Int
getField (X i) = i
setField :: forall f. KnownSymbol f => X -> Int -> X
setField _ = X