You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Haskell类型声明中符号`!`的含义是什么?

Haskell数据类型声明中!符号的作用

!是Haskell中的严格性标注,用于数据类型的字段声明时,强制要求该字段在构造数据值的阶段就被求值到弱首范式(WHNF),覆盖Haskell默认的惰性求值规则。


添加!和不添加的核心差异

我们用下面的示例代码做对比说明:

-- 无严格标注的惰性字段类型
data LazyFoo = LazyFoo { lazyField :: Int } deriving (Show)
-- 加!的严格字段类型
data StrictFoo = StrictFoo { strictField :: !Int } deriving (Show)

两者的差异主要体现在三个方面:

  • 构造时的求值行为不同
    不加!的字段默认是惰性的,构造数据时字段会存储未求值的Thunk(待执行的表达式块),直到程序第一次主动访问该字段时才会实际计算值。例如执行let val = LazyFoo (1 + 2)时,lazyField里存的是1+2的表达式,直到读取lazyField的时候才会计算得到3。
    加!的字段会在构造数据时立刻求值。执行let val = StrictFoo (1 + 2)时,会直接把1+2计算为3存入字段,不需要等到访问的时候才计算。
  • 异常表现不同
    不加!的字段如果传入底层值为⊥(比如undefined、死循环等错误值),只要程序永远不访问这个字段,整个数据值就可以正常使用,不会抛出错误:let val = LazyFoo undefined in "运行正常"这条语句执行不会报错。
    加!的字段如果传入⊥,构造数据的瞬间就会触发求值直接抛出错误:let val = StrictFoo undefined in "运行正常"这条语句执行会直接报错。
  • 内存性能表现不同
    不加!的字段如果长期不被访问,Thunk会持续占用额外内存,大量Thunk堆积就会引发Haskell中常见的空间泄漏问题。
    加!的字段可以避免不必要的Thunk堆积,对于确定一定会被访问的小型字段,能有效降低内存开销,也能规避很多惰性求值带来的意外性能损耗。

补充说明

如果开启了GHC的StrictData扩展,模块内所有自定义数据类型的字段默认都会是严格的,不需要逐个加!;如果需要某个字段保留惰性,可以用~符号做惰性标注。

内容的提问来源于stack exchange,提问作者user16830321

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 05:36:03