为什么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
不需要它?在这里,我来谈谈我的要点: matchInt
和matchDataInt
是否必须针对1进行测试才能进行模式匹配?
答案 0 :(得分:10)
语法matchInt
建立在模式匹配上,但这里的模式匹配是一种错觉。 1
不是数据构造函数。数字文字被重载。 1
相当于fromInteger #1
,其中#1
是Integer
类型的非重载文本(在标准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 Int
和Num 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
已有Eq
和Num
个实例。
另一方面,您的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