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

时间:2017-10-25 05:13:14

标签: haskell equality typeclass ghci algebraic-data-types

为什么ghci在我通过模式匹配构建的函数matchInt的类型签名中列出equality type约束:

$ 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

再次,为什么matchInt函数列表相等作为类型约束而不是函数matchDataInt

question是相关的。但是,如果matchInt需要进行相等性测试,那么为什么matchDataInt不需要它?在这里,我来谈谈我的要点: matchIntmatchDataInt是否必须针对1进行测试才能进行模式匹配?

2 个答案:

答案 0 :(得分:10)

语法matchInt建立在模式匹配上,但这里的模式匹配是一种错觉。 1不是数据构造函数。数字文字被重载。 1相当于fromInteger #1,其中#1Integer类型的非重载文本(在标准Haskell中不可表达)。你无法真正模仿这种事情。

因此,编译器允许您在语法上编写模式匹配,但这种表示法实际上表示一个警卫:

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

由于没有明确给出matchInt的类型,因此推断出它。它是一个函数,因此类型是a->b的一些细化。对fromInteger的调用会产生约束Num a,对==的调用会产生约束Eq a,这就是我们可以告诉的所有内容a

如果OTOH我们给函数一个明确的签名,比如说

matchInt :: Int->Int

然后我们不需要推断类型,只检查它是否满足约束条件。由于Int同时满足Eq IntNum Int,所以一切正常。

这就是你的第二个例子中的内容。您匹配的类型已知为Int,不是因为显式类型签名,而是因为它是Y Int替代Z推断的。这里再次Int已经拥有了所有需要的实例。

答案 1 :(得分:7)

matchDataInt函数不需要Eq约束,因为已在Int 上专门匹配,而Int已经有Eq个实例。

您的matchInt功能并不仅仅将Int作为参数 - 它可以采用任何类型的数字,只要您可以将该数字与平等。这就是它(Eq a, Num a, Num p) => a -> p类型的原因。您也可以为其指定Num p => Int -> p类型(a专门设置Int),因为Int已有EqNum个实例。

另一方面,您的matchDataInt功能会将Z作为参数,每个Z都包含Int。针对的模式匹配Int 会产生Eq约束,但仅限于Int 。你可以改为

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