为什么使用模式匹配构造的函数具有 Eq 类型约束,而在使用数据构造函数时则没有?

Why does a function constructed with pattern matching have the Eq type constraint but not when using a data constructor?

提问人:Rick Majpruz 提问时间:10/25/2017 最后编辑:dfeuerRick Majpruz 更新时间:11/6/2017 访问量:448

问:

为什么 ghci 在我通过模式匹配构造的此函数的类型签名中列出了相等类型约束:matchInt

$ ghci
GHCi, version 8.2.1: http://www.haskell.org/ghc/  :? for help
Prelude> :{
Prelude| matchInt 1 = 3
Prelude| matchInt _ = 4
Prelude| :}
Prelude> matchInt 1
3
Prelude> matchInt 22
4
Prelude> :t matchInt
matchInt :: (Eq a, Num a, Num p) => a -> p

相反,当使用简单的数据构造函数时,没有相等类型约束。

$ ghci
GHCi, version 8.2.1: http://www.haskell.org/ghc/  :? for help
Prelude> data Z = Y Int
Prelude> :{
Prelude| matchDataInt (Y 1) = 3
Prelude| matchDataInt _ = 4
Prelude| :}
Prelude> matchDataInt (Y 1)
3
Prelude> matchDataInt (Y 22)
4
Prelude> :t matchDataInt
matchDataInt :: Num p => Z -> p

事实上,Z 的实例无法进行比较:

Prelude> Y 22 == Y 33
<interactive>:11:1: error:
    • No instance for (Eq Z) arising from a use of ‘==’
    • In the expression: Y 22 == Y 33
      In an equation for ‘it’: it = Y 22 == Y 33

那么,为什么函数将相等性列为类型约束而不是函数?matchIntmatchDataInt

这个问题是相关的。但是,如果需要相等性测试,那么为什么不需要呢?在这里,我来到了我的关键点:不要两者兼而有之,并且必须针对 1 进行测试才能运行模式匹配?matchIntmatchDataIntmatchIntmatchDataInt

haskell 等式 ghci 代数数据类型

评论


答:

7赞 Antal Spector-Zabusky 10/25/2017 #1

该函数不需要约束,因为它专门匹配 Int,并且 s 已经有一个实例。matchDataIntEqIntEq

你的函数不只是把 s 作为参数——它可以接受任何类型的数字,只要你能比较这个数字是否相等。这就是为什么它有类型.您也可以为其指定类型(specializing to ),因为已经有 和 实例。matchIntInt(Eq a, Num a, Num p) => a -> pNum p => Int -> paIntIntEqNum

另一方面,您的函数将 a 作为参数,并且每个函数都包含一个 .针对Int 的模式匹配会产生约束,但仅限于 Int。相反,你可以matchDataIntZZIntEq

data Z' a = Y' a

matchDataNum :: (Eq a, Num a, Num p) => Z' a -> p
matchDataNum (Y' 1) = 3
matchDataNum _      = 4

在这里,您无法删除约束。Eq a


对于本身不返回数字的变体函数,这一切可能会稍微清楚一些。如果我们有

data Z    = Y  Int
data Z' a = Y' a

is1 1 = True
is1 _ = False

isY1 (Y 1) = True
isY1 _     = False

isY'1 (Y' 1) = True
isY'1 _      = False

然后,我们定义的三个函数具有以下类型

is1   :: (Eq a, Num a) => a -> Bool
isY1  :: Z -> Bool
isY'1 :: (Eq a, Num a) => Z' a -> Bool

我们还可以定义

is1Int :: Int -> Bool
is1Int 1 = True
is1Int _ = False
11赞 n. m. could be an AI 10/25/2017 #2

在句法上是建立在模式匹配之上的,但这里的父子匹配是一种错觉。 不是数据构造函数。数值文本已重载。 等价于 where 是 类型的非重载垃圾(在标准 Haskell 中无法表达)。你无法真正与这些东西进行模式匹配。matchInt11fromInteger #1#1Integer

因此,编译器允许你编写语法上的模式匹配,但这种表示法实际上表示守卫:

matchInt 1 = ... -- what is written
matchInt x | x == fromInteger #1 = ...  -- what it really means

由于没有明确给出 的类型,因此可以推断。它是一个函数,因此类型是 的一些细化。调用 产生约束 ,调用 产生约束 ,这就是我们所能讲述的全部内容。matchInta->bfromIntegerNum a==Eq aa

如果 OTOH,我们给函数一个显式签名,比如

matchInt :: Int->Int

那么我们不需要推断类型,只需要检查它是否满足约束。既然同时满足 和 ,一切都很好。IntEq IntNum Int

这就是你的第二个例子中发生的事情。已知匹配的类型是 ,不是因为显式类型签名,而是因为它是从 的替代项推断出来的。这里已经拥有所有需要的实例。IntY IntZInt