你好,
不知道是我弄错了、这是个 bug,还是修改记录更新的去语法糖过程带来的意外结果,但启用 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
按我的理解,trace 外面的 case-of 强制了求值。我通常不介意 Strict 让所有 case 和 let 都严格求值,毕竟能省掉无数个 ! bang 模式,但显式更新的字段既然声明为惰性,这似乎就是 bug 或者无意中的设计结果。
你们怎么看?
从英语翻译·查看原文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?