为什么使用模式匹配构造的函数具有 Eq 类型约束,但在使用数据构造函数时却没有?
Why does a function constructed with pattern matching have the Eq type constraint but not when using a data constructor?
为什么 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
不需要呢?在这里我谈到了我的关键点:难道 both matchInt
和 matchDataInt
必须针对 1 进行模式匹配操作测试吗?
matchDataInt
函数不需要 Eq
约束,因为它 专门匹配 Int
和 Int
s 已经有一个 Eq
个实例。
你的matchInt
函数不只是将Int
s作为参数——它可以采用任何种数字,只要你能比较那个数字是平等的。这就是为什么它的类型为 (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
约束。
对于本身没有 return 数字的变体函数,这一切可能会稍微清楚一些。如果我们有
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
语法上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
,所以一切都ok
这就是您的第二个示例中发生的情况。您匹配的类型已知为 Int
,不是因为显式类型签名,而是因为它是从 Z
的 Y Int
替代项推断出来的。这里再次 Int
已经拥有所有需要的实例。
为什么 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
不需要呢?在这里我谈到了我的关键点:难道 both matchInt
和 matchDataInt
必须针对 1 进行模式匹配操作测试吗?
matchDataInt
函数不需要 Eq
约束,因为它 专门匹配 Int
和 Int
s 已经有一个 Eq
个实例。
你的matchInt
函数不只是将Int
s作为参数——它可以采用任何种数字,只要你能比较那个数字是平等的。这就是为什么它的类型为 (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
约束。
对于本身没有 return 数字的变体函数,这一切可能会稍微清楚一些。如果我们有
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
语法上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
,所以一切都ok
这就是您的第二个示例中发生的情况。您匹配的类型已知为 Int
,不是因为显式类型签名,而是因为它是从 Z
的 Y Int
替代项推断出来的。这里再次 Int
已经拥有所有需要的实例。