[英]Why function (+) matches type (a -> b -> b)?
折叠功能:
fold :: b -> (a -> b -> b) -> [a] -> b
fold z f [] = z
fold z f (x:xs) = f x (fold z f xs)
取自http://www.seas.upenn.edu/~cis194/spring13/lectures/04-higher-order.html
Prelude> :t (+)
(+) :: Num a => a -> a -> a
*Main> fold (0) (+) [1,2,3]
6
什么类型(a -> b -> b)
匹配类型a -> a -> a
for (+)
函数?
由于折叠定义接受函数类型(a -> b -> b)
这意味着前2个参数(a -> b)
必须是不同类型的?
不,这意味着a
和b
可以是不同的,但它并不是强制性的。 在你的情况下,它是相同的。
一个更简单的例子来传达这一点:
data SomeType a b = Test a b deriving (Show)
现在在ghci
:
λ> :t Test
Test :: a -> b -> SomeType a b
λ> let x = Test (3 :: Int) (4 :: Int)
λ> :t x
x :: SomeType Int Int
你在反方向思考。 您不必检查是否+
相同或相匹配a -> b -> b
,你想要的类型+
是一个专业化的 a -> b -> b
和检查这一点,你必须统一类型。
统一意味着您希望通过重命名类型变量来查看+
和类型a -> b -> b
可以相等。
所以+
类型为Num x => x -> x -> x
。 让我们暂时忽略类约束,让我们看看我们是否可以匹配函数类型。 类型变为x -> x -> x
和a -> b -> b
。 事实上,如果我们真实地看待它们,而不使用关联性,那就更好了: x -> (x -> x)
和a -> (b -> b)
。
->
是一个类型构造函数 。 即它是将一定数量的类型映射到不同类型的函数。 在这种情况下, ->
构造函数将两种类型t_1
和t_2
映射到函数类型(->) t_1 t_2
(通常用t_1 -> t_2
)。
所以类型x -> (x -> x)
实际上是(->) x ((->) xx)
,它是类型构造函数->
应用于x
和类型构造函数->
应用于x
和x
。 另一种类型是(->) a ((->) bb)
。
在统一时,考虑两种类型的最外层类型构造函数(在这种情况下,两者都是->
)。 如果这不匹配,则无法统一。 否则你必须统一构造函数的参数。
因此,我们必须统一x
有a
。 它们都是类型变量,因此我们可以重命名其中一个。 比方说,我们重命名a
带有x
。 所以现在我们将重命名应用于类型,获得:( (->) x ((->) xx)
和(->) x ((->) bb)
,你会看到x
和x
现在匹配。
让我们考虑第二个论点。 它不是一个类型变量,所以我们必须匹配类型构造函数,这又是->
两者。 所以我们以递归方式处理参数。
我们想匹配x
和b
。 它们都是类型变量,因此我们可以重命名其中一个。 假设我们将x
重命名为b
。 我们将这种替换应用于类型,获得:( (->) b ((->) bb)
和(->) b ((->) bb)
。 现在一切都匹配。 因此这两种类型统一起来。
关于类约束,当我们用b
重命名x
,我们也将替换应用于约束,因此Num x
变为Num b
,两个最终类型都是Num b => b -> b -> b
。
我希望这能让您更好地了解类型的工作方式以及类型的检查方式。
旁注:这是haskell在执行类型推断时所做的事情。 它首先为未知函数分配一个新类型变量t
。 然后,它使用统一来获取定义它的表达式的类型,并检查与t
关联的类型,这是函数的类型。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.