我试图找到函数(.) map
的类型,但是以某种方式发现它是((a -> d) -> (a -> e)) -> ([d] -> [e])
,根据GHCI,它是不正确的,因为它应该是(.) map :: (a1 -> a2 -> b) -> a1 -> [a2] -> [b]
。
我在做什么错了?
答案 0 :(得分:6)
我们有成分:
:w
(这里我为两个函数使用了不同的类型标识符,以避免造成任何混淆)。更为冗长的形式(在此我们更明确地指出每个函数都精确地使用一个一个参数):
(.) :: (b -> c) -> (a -> b) -> a -> c
map :: (d -> e) -> [d] -> [e]
由于(.) :: (b -> c) -> ((a -> b) -> (a -> c))
map :: (d -> e) -> ([d] -> [e])
是map
的第一个参数,这意味着其类型(.)
应该与(d -> e) -> ([d] -> [e])
函数的输入类型匹配(所以(.)
) 。因此,这意味着:
b -> c
所以这意味着 b -> c
~ (d -> e) -> ([d] -> [e])
------------------------------
b ~ (d -> e), c ~ ([d] -> [e])
的结果类型是:
(.) map
等效于:
(a -> b) -> (a -> c)
或更简单:
(a -> (d -> e)) -> (a -> ([d] -> [e]))
(.) map :: (a -> d -> e) -> a -> [d] -> [e]
函数可以看成(.)
。这意味着我们的功能
(.) f g == \x -> f (g x)
等效于:
h = (.) map
因此,它将函数h f x = map (f x)
和对象f
作为输入,然后执行以x
为函数的map
。
从语义上讲,您可以说我们制作了一个{<1>}类型的“ 映射,其中必须插入一个'contect'-objecct ”。然后,处理器会考虑此上下文。如果我们要应用多个f x
,每个都有很小的变化,并因此首先传递“上下文对象”,则这可能很有用。当然,这是对人类的一种解释。对于编译器,a
可以有任何用途,解释等。
答案 1 :(得分:4)
您可能已经尝试通过查看定义来匹配功能
Types of the two functions
(.) :: ((b -> c) -> (a -> b) -> a -> c)
map :: (d -> e) -> [d] -> [e]
,然后尝试将d
与b
和e
与c
进行匹配。这样可以给您((a -> d) -> (a -> e)) -> ([d] -> [e])
,现在您可以将[d]
与a
进行匹配,并将[e]
与d
进行匹配。但是,这是不正确的,因为根据map
的类型定义,e
和d
可以是不同的类型,即d
可以是[e]
的类型但这不是必须的。
查找此函数类型的正确方法是查看类型的定义
Types of the two functions
(.) :: ((b -> c) -> (a -> b) -> a -> c)
map :: (d -> e) -> [d] -> [e]
,然后将(d -> e)
匹配到b
,将[d] -> [e]
匹配到c
,这将为您提供(a -> (d -> e)) -> a -> ([d] -> [e])
,方法是删除多余的括号并重命名类型变量得到(a -> b -> c) -> a -> [b] -> [c]
。这就是GHCI给您的结果。
答案 2 :(得分:3)
当我不理解函数的类型时,我会使用不同的字母来写类型:
(.) :: (b -> c) -> (a -> b) -> a -> c
map :: (x -> y) -> [x] -> [y]
现在我们提供map
作为(.)
的第一个参数,因此我们可以得出:
b -> c == (x -> y) -> [x] -> [y] -- by matching first arguments we get...
b == x -> y
c == [x] -> [y]
由于我们已经提供了(.)
的第一个参数,所以整个b -> c
部分都消失了。
(.) map :: (a -> b) -> a -> c -- Using the above equations for b and c
(.) map :: (a -> x -> y) -> a -> [x] -> [y] -- changing variables names
(.) map :: (a1 -> a2 -> b) -> a1 -> [a2] -> [b]
为GHCi
情节