首页 文章

感叹号在Haskell声明中意味着什么?

提问于
浏览
232

当我尝试使用真实项目来驱动Haskell时,我遇到了以下定义 . 我不明白每个论点前面的感叹号是什么意思,我的书似乎没有提到它 .

data MidiMessage = MidiMessage !Int !MidiMessage

3 回答

  • 281

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

    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(因为你可以't look inside it without it existing). The first, since we'重新测试它,我们需要一直评估 4 ,我们意识到它不匹配 .

    第二个没有测试它 . 因此,我们只是存储代码以供稍后评估,而不是 6 存储在该存储器位置中,而不是 (3+3) . 只有当有人看着它时,它才会变成6 .

    但是,第三个参数前面有一个 ! ,因此严格评估: (4+4) 被执行, 8 存储在该内存位置 .

    第四个参数也是严格评估的 . 但是这里's where it gets a bit tricky: we'重新评估不完全,但只是弱正常的头形式 . 这意味着我们弄清楚它是 Nothing 还是 Just ,然后存储,但我们不再进一步了 . 这意味着我们不存储 Just 10 但实际上 Just (5+5) ,将thunk留在未评估状态 . 重要的是要知道,尽管我认为这样做的所有含义都超出了这个问题的范围 .

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

    f x !y = x*y
    

    f (1+1) (2+2) 将返回thunk (1+1)*4 .

  • 24

    查看strict和非严格构造函数参数之间区别的一种简单方法是它们在未定义时的行为方式 . 特定

    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
    
  • 76

    我相信这是一个严格的注释 .

    Haskell是一种纯粹而懒惰的函数式语言,但有时懒惰的开销可能过多或浪费 . 因此,为了解决这个问题,您可以要求编译器完全评估函数的参数,而不是解析thunk周围的问题 .

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

相关问题