Привет,
Не знаю, ошибаюсь ли я, это баг или неожиданное следствие изменения десахаризации обновлений записей, но обновление ленивого поля записи при включённом Strict, похоже, форсирует его. При создании записи это поле не форсируется.
Вот пример теста:
{-# LANGUAGE Strict, OverloadedRecordDot #-}
import Prelude hiding (id)
import Debug.Trace
data Member = Member
{id :: Int, years :: ~Int}
main = do
let member = Member {id = 17, years = 39}
let revised = member {years = trace "FORCED" 46}
print revised.id
С GHC v9.12.4 выводится “FORCED”. С v9.2.8 этого нет. Отключение Strict, конечно, тоже предотвращает вывод.
Приблизительный Core выглядит так
case trace "FORCED" 46 of years
__DEFAULT -> case member of
Member id _ -> Member id years
и, насколько я понимаю, case-of вокруг trace форсирует вычисление. В целом я не против того, чтобы Strict делал все case и let строгими, избавляя от бесчисленных bang-паттернов !, но когда явно обновляемое поле объявлено ленивым, это выглядит как баг или случайное следствие дизайна.
Что вы об этом думаете?
Переведено с английского·Показать оригиналHey,
I can't tell whether I'm mistaken, this is a bug, or changing record-update desugaring produced a surprising consequence, but updating a lazy record field with Strict enabled seems to force it. Constructing the record doesn't force that field.
Here's a sample test:
{-# LANGUAGE Strict, OverloadedRecordDot #-}
import Prelude hiding (id)
import Debug.Trace
data Member = Member
{id :: Int, years :: ~Int}
main = do
let member = Member {id = 17, years = 39}
let revised = member {years = trace "FORCED" 46}
print revised.id
With GHC v9.12.4, “FORCED” appears. It doesn't with v9.2.8. Disabling Strict also prevents it, of course.
The approximate core looks like
case trace "FORCED" 46 of years
__DEFAULT -> case member of
Member id _ -> Member id years
and, as I understand it, the case-of around trace forces evaluation. I'm generally happy for Strict to make every case and let strict (sparing us countless ! bang patterns), but when the field being explicitly updated was declared lazy, this feels like a bug or an accidental design consequence.
How does that look to you?