Haskell:Num 类型的模式匹配

Gui*_*ume 4 haskell pattern-matching

为什么 Haskell 不能在Num类型上执行模式匹配,而不需要我们指定Eq为类型类?

例如:

h :: Num a => a -> a
h 0 = -1
h x = x + 1
Run Code Online (Sandbox Code Playgroud)

编译这个函数时,ghci抱怨:

    * Could not deduce (Eq a) arising from the literal `0'
      from the context: Num a
        bound by the type signature for:
                   h :: forall a. Num a => a -> a
        at functions.hs:9:1-20
      Possible fix:
        add (Eq a) to the context of
          the type signature for:
            h :: forall a. Num a => a -> a
    * In the pattern: 0
      In an equation for `h': h 0 = - 1
   |
10 | h 0 = -1
   |   ^
Run Code Online (Sandbox Code Playgroud)

更改函数定义如下,编译运行完美:

h :: (Num a, Eq a) => a -> a
h 0 = -1
h x = x + 1

*Main> h 0
-1
*Main>
Run Code Online (Sandbox Code Playgroud)

Chr*_*tin 5

Haskell 2010 报告中,标题为模式匹配的非正式语义的部分:

如果v k,则将数字、字符或字符串文字模式k与值v匹配成功 ==

因此,当您使用文字(例如0)作为模式时,其含义取决于==Eq类的方法)。

例如,您的功能 h

h 0 = -1
h x = x + 1
Run Code Online (Sandbox Code Playgroud)

可以改写为

h x | x == 0 = -1
h x          = x + 1
Run Code Online (Sandbox Code Playgroud)

您正在(隐式)使用该==方法,因此您需要一个Eq约束。

关于 Haskell 与许多其他语言的不同之处,这里有两个重要的观察:

  1. 并非所有类型都定义了相等的概念。人们不能询问是否x == y除非类型xy有一个Eq实例。
  2. 数值类型的集合不是固定的。数字文字可以采用具有 实例的任何类型Num。您可以定义自己的类型并使其成为 的实例Num,并且它不一定还具有 的实例Eq。所以并不是所有的“数字”都可以比较相等。

所以你的函数的上下文h是“a必须是一个数字”是不够的。更具体地说,上下文必须是“必须是a具有相等性测试的数字”,以确保有一种方法可以检查是否x等于0以执行模式匹配。

  • @Davislor 我看到到处引用的例子是“你可以为函数创建一个合理的实例”(例如https://wiki.haskell.org/Num_instance_for_functions),并且函数*不能*有一个好的` eq`实例。但这通常伴随着警告“你可能不应该使用那个 `Num` 实例”。想到的另一种可能性是表示带有变量的表达式的数据类型。`Num` 中的操作实际上与 `Eq` 中的操作没有任何关系,所以说当你不能在不提供另一个的情况下拥有一个时,它没有现在那么有用。 (2认同)
  • @Davislor GHC 确实让您深入了解运行时内部;查看 [this answer](/sf/answers/1299465261/) 中定义的 `unsafeAddr` 函数。但是,即使您确实通过表示它们的内存地址来比较函数,它仍然不是*好的* 相等性测试,因为在很多情况下,当您比较两种不同的表示形式时,它会产生“假”什么是真正相同的功能。如果你想要“引用相等”,正确的方法是使用 `IORef`(它有一个 `Eq` 实例)或类似的东西。 (2认同)