Haskell 如何避免布尔值定义中的永无休止的递归?

lon*_*ero 6 haskell boolean

几年前,我读到 Haskell 如何比较布尔表达式,我想看看是否可以在我的项目(F#)中使用相同的概念,但我不明白它是如何工作的:

来源: https: //hackage.haskell.org/package/ghc-prim-0.10.0/docs/src/GHC.Classes.html#%3D%3D

x /= y               = not (x == y)
x == y               = not (x /= y) 
Run Code Online (Sandbox Code Playgroud)

这是完全有道理的,但是它怎么不会以永无休止的递归结束呢?


背景:在我的项目中,我已经花费和未花费的硬币,我认为它们可以像布尔值一样处理,当硬币没有花费时,它是未花费的,当它没有未花费时,它是花费的。

sep*_*p2k 12

这些是类型类的默认方法Eq。这意味着如果某种类型实例化Eq并且不提供 的实现==,它将被定义为not (x /= y)。如果它没有提供 的实现/=,它将被定义为not (x == y)。如果它不提供任一方法的实现,您确实会得到无限递归。

另请注意{-# MINIMAL (==) | (/=) #-}这两行后面的注释。Eq这意味着“将定义==或”的最小实现/=,并指示编译器在您创建未定义至少其中一个方法的实例时生成警告。


Nou*_*are 9

如果您像这样声明普通函数,它将无限循环。但在本例中,这些只是类方法的默认定义。至少其中一个应该被此类的每个特定实例覆盖(这就是所说的{-# MINIMAL (==) | (/=) #-})。

这种情况是 CLC 提案的主题:https://github.com/haskell/core-libraries-committee/issues/3。因此,将来这种情况可能会通过简单地(/=)从类中删除该方法并使其成为一个单独的函数来解决。


Wil*_*sem 8

这是完全有道理的,但是它怎么不会以永无休止的递归结束呢?

这是一个类型类,实例应该进行实现。您看到的是默认(后备)实现。

事实上,编译器通常会强制执行这一点。该类不仅提供函数签名和默认实现,还显示:

class Eq a where
  -- …
  {-# MINIMAL (==) | (/=) #-}
Run Code Online (Sandbox Code Playgroud)

最小意味着类型类的实例必须实现(==)(/=)(当然可以两者都实现)。

事实上,对于 的任何有意义的实例也是如此Eq,例如Int

instance Eq Int where
    (==) = eqInt
    (/=) = neInt
Run Code Online (Sandbox Code Playgroud)

其中eqIntneInt在本例中实现为:

eqInt, neInt :: Int -> Int -> Bool
(I# x) `eqInt` (I# y) = isTrue# (x ==# y)
(I# x) `neInt` (I# y) = isTrue# (x /=# y)
Run Code Online (Sandbox Code Playgroud)

然后,它们具有==#可处理未装箱 Int#值的函数。

请注意, Haskell 中的类型类,这看起来更像是面向对象编程中的接口。它不处理(具体)数据,它只是提供一个应该实现的接口。