[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