# Strict extension unexpectedly forcing lazy record field assignment

**URL:** <https://discourse.haskell.org/t/strict-extension-unexpectedly-forcing-lazy-record-field-assignment/14748>\
**Category:** Learn\
**Created:** [September 26, 2026, 2:09pm UTC](https://discourse.haskell.org/t/strict-extension-unexpectedly-forcing-lazy-record-field-assignment/14748 "2026-09-26T14:09:32Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![moll](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/moll/32/5771_2.png) [@moll](https://discourse.haskell.org/u/moll)\
**Post date:** [September 26, 2026, 2:09pm UTC](https://discourse.haskell.org/t/strict-extension-unexpectedly-forcing-lazy-record-field-assignment/14748/1 "2026-09-26T14:09:33Z")

</div>

Hey,

I’m not sure if it’s me, a bug or just an unexpected outcome when record updating desugaring was changed, but I’m seeing a lazy record field being forced when updating it under the `Strict` extension. It’s not forced when constructing the record.

For example, given this test:

```haskell
{-# LANGUAGE Strict, OverloadedRecordDot #-}
import Prelude hiding (id)
import Debug.Trace

data Person = Person {id :: Int, age :: ~Int}

main = do
  let person = Person {id = 13, age = 37}
  let updated = person {age = trace "FORCED" 42}
  print updated.id

```

GHC v9.12.4 prints out “FORCED”. v9.2.8 doesn’t. Naturally, without `Strict` it doesn’t either.

The core output is roughly

```haskell
case trace "FORCED" 42 of age
  __DEFAULT -> case person of
    Person id _ -> Person id age

```

with the case-of of trace forcing it if I understand correctly. I’m fine with `Strict` in general causing all cases and lets to be strict (saving us from typing a million `!` bang patterns), but if we’re explicitly updating a record field set to be lazy, it sounds like either a bug or an unintended design result.

What do you think?

---

<div class="post-metadata">

**Author:** ![tomjaguarpaw](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/tomjaguarpaw/32/1230_2.png) [@tomjaguarpaw](https://discourse.haskell.org/u/tomjaguarpaw)\
**Post date:** [September 26, 2026, 3:07pm UTC](https://discourse.haskell.org/t/strict-extension-unexpectedly-forcing-lazy-record-field-assignment/14748/2 "2026-09-26T15:07:30Z")

</div>

It seems this first occurs in 9.6, perhaps because of “[Record updates for GADTs and other existential datatypes are now fully supported](https://downloads.haskell.org/ghc/9.6.1/docs/users_guide/9.6.1-notes.html)”. Have a look at [`Note [Record Updates]` in the type checker source](https://downloads.haskell.org/~ghc/9.6.7/docs/libraries/ghc-9.6.7/src/GHC.Tc.Gen.Expr.html). It says

```haskell
To typecheck a record update, we desugar it first. Suppose we have
    data T p q = T1 { x :: Int, y :: Bool, z :: Char }
               | T2 { v :: Char }
               | T3 { x :: Int }
               | T4 { p :: Float, y :: Bool, x :: Int }
               | T5
Then the record update `e { x=e1, y=e2 }` desugars as follows

       e { x=e1, y=e2 }
    ===>
       let { x' = e1; y' = e2 } in
       case e of
          T1 _ _ z -> T1 x' y' z
          T4 p _ _ -> T4 p y' x'

```

That is, strict `let`s are pulled out during type checking. I believe what happened is that in 9.4 and earlier this transformation happened when desugaring to Core, where Strict couldn’t get at it, now it happens in type checking, where Strict can get at it.

I think this is a bug. In `let updated = person { ... }`, `updated` should be evaluated to its constructor, which doesn’t require evaluating the new lazy fields in the record update itself. I’m surprised this hasn’t been noticed before.

---

<div class="post-metadata">

**Author:** ![simonpj](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/simonpj/32/890_2.png) [@simonpj](https://discourse.haskell.org/u/simonpj)\
**Post date:** [September 26, 2026, 6:31pm UTC](https://discourse.haskell.org/t/strict-extension-unexpectedly-forcing-lazy-record-field-assignment/14748/3 "2026-09-26T18:31:22Z")

</div>

Looks like a bug to me. Would one of you like to open a ticket? Thanks!

---

<div class="post-metadata">

**Author:** ![moll](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/moll/32/5771_2.png) [@moll](https://discourse.haskell.org/u/moll)\
**Post date:** [September 26, 2026, 7:25pm UTC](https://discourse.haskell.org/t/strict-extension-unexpectedly-forcing-lazy-record-field-assignment/14748/4 "2026-09-26T19:25:18Z")

</div>

Thanks for the confirmation, both! I’ll jump over to GHC’s issue tracker tomorrow!
