感叹号在Haskell声明中是什么意思?

问题描述 投票:253回答:3

当我尝试使用一个实际的项目来驱动Haskell时,我遇到了以下定义。我不明白每个论点前面的感叹号的含义,而且我的书中似乎都没有提到它。

data MidiMessage = MidiMessage !Int !MidiMessage
haskell syntax lazy-evaluation
3个回答
305
投票

这是一个严格的声明。基本上,这意味着在创建数据结构值时必须将其评估为所谓的“弱头范式”。让我们看一个例子,以便我们可以看到这意味着什么:

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"

这将执行足够的代码来完成所需的工作,仅此而已。因此它将创建具有四个参数的Foo(因为如果没有它,您将无法在其中查看内容)。首先,因为我们正在测试它,所以我们需要一直评估到4,直到我们意识到它不匹配。

不需要评估第二个,因为我们没有对其进行测试。因此,不是将6存储在该内存位置中,而是将代码存储起来,以便以后进行评估(3+3)。仅当有人看时,它就会变成6。

但是,第三个参数前面带有!,因此必须严格评估:执行(4+4),并且8存储在该存储位置。

第四个参数也经过严格评估。但是,这里有些棘手:我们没有完全评估,而只是评估弱势的正常头部。这意味着我们找出是Nothing还是Just的东西,并将其存储起来,但是我们走得更远了。这意味着我们不存储Just 10而是实际存储Just (5+5),从而使内部的thunk未被评估。知道这一点很重要,尽管我认为这的所有含义都超出了此问题的范围。

如果启用BangPatterns语言扩展,则可以用相同的方式注释函数参数:

f x !y = x*y

[f (1+1) (2+2)将返回重击(1+1)*4


89
投票

查看严格和非严格构造函数参数之间区别的一种简单方法是,当它们未定义时它们的行为。给出

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

26
投票

我相信这是一个严格的注解。

Haskell是一种纯净的lazy功能性语言,但是有时懒惰的开销可能太多或浪费。因此,为了解决这个问题,您可以要求编译器完全评估函数的参数,而不是分析周围的重击。

此页面上有更多信息:Performance/Strictness

© www.soinside.com 2019 - 2024. All rights reserved.