提问人:Rick Majpruz 提问时间:10/25/2017 最后编辑:dfeuerRick Majpruz 更新时间:11/6/2017 访问量:448
为什么使用模式匹配构造的函数具有 Eq 类型约束,而在使用数据构造函数时则没有?
Why does a function constructed with pattern matching have the Eq type constraint but not when using a data constructor?
问:
为什么 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
那么,为什么函数将相等性列为类型约束而不是函数?matchInt
matchDataInt
这个问题是相关的。但是,如果需要相等性测试,那么为什么不需要呢?在这里,我来到了我的关键点:不要两者兼而有之,并且必须针对 1 进行测试才能运行模式匹配?matchInt
matchDataInt
matchInt
matchDataInt
答:
该函数不需要约束,因为它专门匹配 Int
,并且 s 已经有一个实例。matchDataInt
Eq
Int
Eq
你的函数不只是把 s 作为参数——它可以接受任何类型的数字,只要你能比较这个数字是否相等。这就是为什么它有类型.您也可以为其指定类型(specializing to ),因为已经有 和 实例。matchInt
Int
(Eq a, Num a, Num p) => a -> p
Num p => Int -> p
a
Int
Int
Eq
Num
另一方面,您的函数将 a 作为参数,并且每个函数都包含一个 .针对该 Int
的模式匹配会产生约束,但仅限于 Int
。相反,你可以matchDataInt
Z
Z
Int
Eq
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
在句法上是建立在模式匹配之上的,但这里的父子匹配是一种错觉。 不是数据构造函数。数值文本已重载。 等价于 where 是 类型的非重载垃圾(在标准 Haskell 中无法表达)。你无法真正与这些东西进行模式匹配。matchInt
1
1
fromInteger #1
#1
Integer
因此,编译器允许你编写语法上的模式匹配,但这种表示法实际上表示守卫:
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
评论