
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.