折叠功能:
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
函数的类型(+)
匹配是什么?
由于折叠定义接受函数类型(a -> b -> b)
,这意味着前2个参数(a -> b)
需要具有不同类型?
答案 0 :(得分:15)
不,这意味着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
答案 1 :(得分:3)
你在反方向思考。您无需检查+
是否相同或匹配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 ((->) x x)
,它是应用于->
的类型构造函数x
以及应用于{{1的类型构造函数->
}和x
。
另一种类型是x
。
统一时,考虑两种类型的最外层类型构造函数(在这种情况下,两者都是(->) a ((->) b b)
)。如果这不匹配,则无法统一。
否则你必须统一构造函数的参数。
因此,我们必须将->
与x
统一起来。它们都是类型变量,因此我们可以重命名其中一个。假设我们使用a
重命名a
。现在我们将重命名应用于类型,获取:x
和(->) x ((->) x x)
,您会看到(->) x ((->) b b)
和x
现在匹配。
让我们考虑第二个论点。它不是一个类型变量,所以我们必须匹配类型构造函数,这两者都是x
。所以我们以递归方式处理论证。
我们希望匹配->
和x
。它们都是类型变量,因此我们可以重命名其中一个。假设我们将b
重命名为x
。我们将此替换应用于类型,获取:b
和(->) b ((->) b b)
。现在一切都匹配。因此这两种类型统一起来。
关于类约束,当我们使用(->) b ((->) b b)
重命名x
时,我们也将替换应用于约束,因此b
变为Num x
,两个最终类型都是{ {1}}。
我希望这能让您更好地了解类型的工作方式以及如何检查类型。
旁注:这是haskell在执行类型推断时所做的事情。它首先为未知函数分配一个新的类型变量Num b
。然后它使用统一来获取定义它的表达式的类型,并检查与Num b => b -> b -> b
关联的类型,这是函数的类型。