[Haskell-cafe] Cannot update a field in a record with a polymorphic type.
Viktor Dukhovni
ietf-dane at dukhovni.org
Sun Sep 6 11:35:50 UTC 2020
On Sun, Sep 06, 2020 at 02:47:58PM +0500, Ignat Insarov wrote:
> Consider this conversation with GHC:
>
> λ data Y α = Y {y ∷ α}
> λ defaultY = Y {y = mempty}
> λ :type defaultY
> defaultY :: Monoid α => Y α
> λ :type defaultY {y = "c"} ∷ Y String
The mistake is to think that the expression:
record { field = value }
modifies *that* input record. But in fact, it should be clear that it
produces a *new* record (Haskell values are immutable). If the record
type is polymorphic:
data Record a = Record { field :: a } deriving Show
we can write:
update :: Record a -> b -> Record b
update record b = record { field = b }
which produces an output whose type is different from the input. We
thus observe:
Prelude> data Record a = Record { field :: a } deriving Show
Prelude> :{
Prelude| update :: Record a -> b -> Record b
Prelude| update record b = record { field = b }
Prelude| :}
Prelude> let x = Record 1
Prelude> x
Record {field = 1}
Prelude> let y = update x "foo"
Prelude> y
Record {field = "foo"}
This is why the type annotation on the output (easily inferred) is not
sufficient, and it is the input "defaultY" that needs an explicit type.
You can of course write a type-preserving setter:
monoUpdate :: Record a -> a -> Record a
monoUpdate record a = record { field = a}
with monoUpdate, your example works:
Prelude> let defaultR = Record mempty
Prelude> :{
Prelude| monoUpdate :: Record a -> a -> Record a
Prelude| monoUpdate record a = record { field = a}
Prelude| :}
Prelude> monoUpdate defaultR "c"
Record {field = "c"}
the type morphing behaviour of field update setters can admittedly be
surprising at first encounter.
--
Viktor.
More information about the Haskell-Cafe
mailing list