感叹号在Haskell声明中意味着什么?
当我试图通过一个真正的项目来学习Haskell来驱动它时,我遇到了下面的定义。 我不明白每个争论前面的惊叹号是什么意思,我的书似乎没有提到它。
data MidiMessage = MidiMessage !Int !MidiMessage
这是一个严格的声明。 基本上,这意味着在创build数据结构值时,必须对所谓的“弱正常头部forms”进行评估。 我们来看一个例子,以便我们看到这意味着什么:
data Foo = Foo Int Int !Int !(Maybe Int) f = Foo (2+2) (3+3) (4+4) (Just (5+5))
上面的函数f
在计算时会返回一个“thunk”:即要执行的代码来计算出它的值。 那时,Foo甚至还不存在,只是代码。
但是在某些时候,有人可能会试图去查看它,可能是通过模式匹配:
case f of Foo 0 _ _ _ -> "first arg is zero" _ -> "first arge is something else"
这将执行足够的代码来完成它所需要的function,而不再需要。 所以它会创build一个具有四个参数的Foo(因为如果没有这个参数,就不能查看它)。 首先,因为我们正在testing它,所以我们需要评估一直到4
,我们认识到它不匹配。
第二个不需要评估,因为我们没有testing它。 因此,而不是存储在那个内存位置,我们只是存储可能的后续评估代码(3+3)
。 只有当有人看着它时,它才会变成6。
然而第三个参数有一个!
在它的前面,所以严格评估: (4+4)
被执行,并且8
被存储在那个存储位置。
第四个参数也被严格评估。 但是这里有一些棘手的问题:我们不是完全评估,而只是评估正常的头部形态。 这意味着我们要弄清楚它是不是Nothing
也不是Just
一些东西,然后把它存储起来,但是我们不会再去了。 这意味着我们不仅存储Just 10
而且实际上Just (5+5)
,使得内部未被评估。 这一点很重要,但我认为这个问题的所有影响都超出了这个问题的范围。
如果启用BangPatterns
语言扩展,则可以用同样的方法注释函数参数:
fx !y = x*y
f (1+1) (2+2)
将返回thunk (1+1)*4
。
查看严格构造函数参数和非严格构造函数参数的区别的一个简单方法是在未定义时的行为。 特定
data Foo = Foo Int !Int first (Foo x _) = x second (Foo _ y) = y
由于非严格的参数不是以second
为单位进行评估,所以传入undefined
不会导致问题:
> second (Foo undefined 1) 1
但严格的论点不能被undefined
,即使我们不使用这个值:
> first (Foo 1 undefined) *** Exception: Prelude.undefined
我相信这是一个严格的诠释。
Haskell是一种纯粹而懒惰的函数式语言,但有时懒惰的开销可能太多或浪费。 所以要处理这个问题,你可以要求编译器充分评估一个函数的参数,而不是分析周围的东西。
有更多的信息在这个页面上: 性能/严格性 。