更自然地表达断言

时间:2014-04-11 19:57:27

标签: haskell pattern-matching assertions

假设我写了一个函数

f [x, y] = x + y
f [x, y, z] = z - x - y

这是由编译器用额外的行填写的,例如

f _ = error "pattern match failed"

如果f未导出,而我知道它只能正确应用,并且该功能对性能至关重要,我可能希望避免使用额外的模式生产代码。我可以重写这个非常不自然的东西,比如

f l = assert (atLeastTwo l) $
        let (x,r1) = (unsafeHead l, unsafeTail l) in
          let (y,r2) = (unsafeHead r1, unsafeTail r1) in
            case r2 of
              [] -> x + y
              (z,r3) -> assert (r3 == []) $ z - x - y 

f _ = makeDemonsComeOutOfMyNose "This error is impossible."

当断言或推断的安全Haskell被启用时,描述性命名的魔法函数将编译为error,并在禁用断言时标记为无法访问(使模式匹配不安全)。有没有办法做到这一点,或类似的东西?

修改

解决jberryman对是否存在真正的性能影响的担忧:

  1. 这是一个假设的问题。我怀疑在更复杂的情况下,有多个"不会发生"在某些情况下,可能会有性能优势 - 至少,错误情况可能会在指令缓存中占用额外的空间。

  2. 即使不是真正的性能问题,我认为断言和错误之间也存在表达的区别。我怀疑最灵活的断言形式是"这段代码应该是无法访问的,而且可能带有一个或三个参数,表明编译器应该认真对待这个声明。安全性是相对的 - 如果数据结构不变被破坏并导致程序泄露机密信息,那么不一定比无效的存储器访问更严重。请注意,粗略地说,assert p x = if p then x else makeDemonsFlyOutOfMyNose NO_REAL_DEMONS_PLEASE "assertion failed",但没有办法根据assert定义恶魔函数。

1 个答案:

答案 0 :(得分:3)

GHC非常聪明,可以优化未使用的模式匹配。这是一个简单的程序。

module Foo (foo) where

data List a = Nil | Cons a (List a)

link :: List a -> List a -> List a
link Nil        _   = error "link: Nil"
link (Cons a _) xs  = Cons a xs

l1 = Cons 'a' (Cons 'b' Nil)

foo = link l1

这是一个非常人为的例子,但它证明了GHC可以证明link(或者在你的情况下是f)被静态知道的构造函数调用的情况(或者可以证明哪个模式匹配将通过内联,简化等方式成功。)

这是核心输出:

foo1 :: Char
foo1 = C# 'a'

foo :: List Char -> List Char
foo = \ (ds :: List Char) -> Cons foo1 ds

error案例未显示在核心Foo的任何位置。因此,您可以放心,在这种情况下,通过额外的未使用模式匹配绝对没有性能差异。