这里有很多线程来推导组合函数的推断类型,但我仍然相当困惑。我发现的帖子都没有给出关于如何统一类型的一般性解释。
我的一本考试指南中存在问题,而我无法搞清楚。
8)什么是(。)地图的推断类型uncurry :: ________
我能够在大多数时间推导推断类型,但我仍然有点困惑。例如,我知道要获得(。)map uncurry的答案,您需要首先派生地图类型uncurry。我能够做到这一点
鉴于以下类型
map :: (a -> b) -> [a] -> [b]
uncurry :: (a -> b -> c) -> (a, b) -> c
我将地图中的函数(a - > b)统一为uncurry so
a = a → b → c
b = (a, b) → c
然后答案是地图的另一半[a] - > [b]使用a和b的新值
map uncurry :: [ a -> b -> c ] -> [ (a, b) -> c]
然后你需要统一
(.) :: (b -> c) -> (a -> b) -> a -> c
map uncurry :: [ a -> b -> c ] -> [ (a, b) -> c]
答案应该是
(.) map uncurry :: (a -> b1 -> b) -> [(a, b1)] -> [b]
但我不明白b1来自哪里或基本上是如何完成的。 我认为我真正需要的是对一般类型统一的解释。统一两种类型的方法是什么?如何知道两种类型不能统一。
如果有人能够逐步解释如何导出(。)地图不合理,我会非常感激。
答案 0 :(得分:6)
->
与右侧相关联。让我们通过你的榜样。
<强>类型强>
(.) :: (b -> c) -> (a -> b) -> a -> c
map :: (a -> b) -> [a] -> [b]
uncurry :: (a -> b -> c) -> (a, b) -> c
为每个函数指定不重叠的类型名称
首先,这很令人困惑,因为有很多a
s并且它们都不是同一个东西,所以我要用新字母重命名这些类型时间。
(.) :: (b -> c) -> (a -> b) -> a -> c
map :: (d -> e) -> [d] -> [e]
uncurry :: (f -> g -> h) -> (f, g) -> h
对类型进行括号,与右侧相关联
(.) :: (b -> c) -> ((a -> b) -> a -> c)
map :: (d -> e) -> ([d] -> [e])
uncurry :: (f -> (g -> h)) -> ((f, g) -> h)
现在让我们看一下(.) map uncurry
这个词。正如您现在意识到的那样,将运算符.
放在括号中会将其转换为遵循正常函数规则的函数,因此
(.) map uncurry
表示((.) map) uncurry
,首先要统一的类型来自(.)
和map
。
现在(.)
有第一个参数(b->c)
,因此(b->c)
必须与map
的类型统一:
(.) :: ( b -> c ) -> ((a -> b) -> (a -> c))
map :: (d -> e) -> ([d] -> [e])
当我们将b ~ (d->e)
和c ~ ([d]->[e])
替换为我们获得的(.)
类型时:
(.) :: ((d->e) -> ([d]->[e])) -> ((a -> (d->e)) -> (a -> ([d]->[e])))
所以map
成为第一个参数,因此当我们提供它时,它会从类型签名中消失,给出
(.) map :: ((a -> (d->e)) -> (a -> ([d]->[e])))
检查解释器,如ghci或拥抱
Hugs> :t (.) map
(map .) :: (a -> b -> c) -> a -> [b] -> [c]
是的 - 当我们添加由->
为右关联的括号时,它们是相同的。
好的,现在我们有了
(.) map :: ((a -> (d->e)) -> (a -> ([d]->[e])))
uncurry :: (f -> (g -> h)) -> ((f, g) -> h)
现在它非常诱人地匹配这两个第一个参数,因为它们看起来相同,但当然我们需要将(.) map
的第一个参数与整个类型的uncurry
:
将第一个参数的类型与整个参数的类型匹配
(.) map :: (( a -> ( d -> e)) -> (a -> ([d]->[e])))
uncurry :: (f->(g->h)) -> ((f,g) -> h)
提供a ~ (f->(g->h))
,d ~ (f,g)
和e ~ h
:
(.) map :: (((f->(g->h)) -> ((f,g)-> h)) -> ((f->(g->h)) -> ([(f,g)]->[h])))
并将其应用于uncurry给出了
(.) map uncurry :: ((f->(g->h)) -> ([(f,g)]->[h])))
与口译员核实
Hugs> :t (.) map uncurry
map . uncurry :: (a -> b -> c) -> [(a,b)] -> [c]
太棒了 - 我们做到了!
如果我们采用示例length . map (.) $ repeat id ++ [] ++ []
,我们将需要所有这些运算符的固定性,但我们可以先将函数应用程序括起来,因为它们优先:
length . map (.) $ repeat id ++ [] ++ []
length . (map (.)) $ (repeat id) ++ [] ++ []
按优先顺序排列运算符
使用解释器的:i
命令查找运算符的固定性,并按顺序排列,最高位:
infixr 9 .
infixr 5 ++
infixr 0 $
最高优先级运算符.
首先被括起来:
(length . (map (.))) $ (repeat id) ++ [] ++ []
然后是++
,它与右边相关联:
(length . (map (.))) $ ((repeat id) ++ ([] ++ []))
并且$
只使用了++
,所以我并不打算将它包围起来。
如果您愿意,可以将(++)
等运算符转换为函数{{1}},但我完全不相信会对您有所帮助,所以我会留下它,只记得第一个参数在左边。
它通常有助于从最嵌套的括号开始。
答案 1 :(得分:0)
(.) map uncurry
相当于((.) map) uncurry
。 map
功能未应用于uncurry
,会传递给(.)
。然后,您希望推断为函数的结果将uncurry
作为参数接收。
至于b1
的来源,不要忘记类型变量名称并不重要,只要您同样重命名给定类型变量的所有匹配项,就可以重命名它们。所以这个:
(.) map uncurry :: (a -> b1 -> b) -> [(a, b1)] -> [b]
相当于:
(.) map uncurry :: (apple -> pear -> plum) -> [(apple, pear)] -> [plum]