【问题标题】:Comparing Two Mutually Recursive Values for Equality in HSpec is Causing Problems在 HSpec 中比较两个相互递归的相等值会导致问题
【发布时间】:2016-09-13 00:35:14
【问题描述】:

1 背景

考虑 Haskell 的以下 sn-p:

data T = T { f1 :: String 
           , f2 :: T
           } deriving (Eq, Show)

r1 = T { f1 = "val1"
       , f2 = r2 
       } :: T

r2 = T { f1 = "val2"
       , f2 = r1
       } :: T

注意r1 是如何相对于r2 相互递归的。 AFAIK,这在 Haskell 中完全没问题(只要你不使用这两个值导致相互递归的调用循环永远不会终止)。例如,r2 的用户可能希望通过f1 (f2 r2)(在本例中为“val1”)访问值f1 r1。 (事实上​​,这就是我尝试在自己的代码中使用这些类型的数据结构的方式。)

2 问题

现在考虑以下 HSpec 单元测试:

describe "A mutually recursive value should be equal to itself" $ do
    r1 `shouldBe` r1

这会导致测试编译器停止一段时间,然后最终失败。这是否意味着我的代码有问题,或者这只是 shouldBe 方式的怪癖已定义(我假设它检查相等性的方式不是懒惰的,并且涉及相互递归的非终止调用)?如果这只是 HSpec 的 shouldBe 函数的一个怪癖,那么有没有办法检查相互递归值之间的相等性?我最终希望能够做的事情如下:

r3 = T { f1 = "val1"
       , f2 = r2 
       } :: T

describe "A mutually recursive value should be equal to itself" $ do
    r1 `shouldBe` r3

【问题讨论】:

  • 问题是你的 Eq 实例(懒惰无关紧要 - shouldBe 当然会迫使他们得到 Bool 结果)(和 Show 也一样)那些会循环永远 - 所以你应该自己实现这些 - 使用某种形式的 loop - 检测或根本不使用递归值

标签: haskell mutual-recursion hspec


【解决方案1】:

您无法在有限的时间内比较无限的值。

确实,比较无限值通常是无法确定的,因此编译器不可能生成适用于这些值的 Eq 实例。

为了强调这一点,请考虑以下代码:

complexCondition :: Integer -> Bool
complexCondition = ...

foo :: Integer -> T
foo n | complexCondition n = T { f1 = "found!", f2 = foo (n+1) }
      | otherwise          = T { f1 = "not yet", f2 = foo (n+1) }

reference :: T
reference = T { f1 = "not yet", f2 = reference }

我们将有foo 0 == reference 是且仅当complexCondition n 为假对于所有n。这是不可行的检查。

但是,由于T 上的比较问题是核心(它的补码是递归可枚举的),那么我们可以半决定补码。实际上,生成的Eq 实例将在有限时间内针对不同的Ts 返回False。这是可行的,因为只需要检查此类值的有限前缀即可声明 False

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-26
    • 1970-01-01
    • 1970-01-01
    • 2013-08-18
    • 2012-03-03
    • 1970-01-01
    相关资源
    最近更新 更多