导出组合函数的推断类型Haskell:特别是(.)map uncurry
这里有很多关于导出组合函数的推断类型的线程,但我仍然相当困惑。我发现的所有帖子都没有对如何统一类型给出一般性的解释 我的一本考试学习指南有问题,我很难弄清楚 8) (.)map uncurry的推断类型是什么::\uuuuuuuuuuuuuuuuuuuuuu 我大部分时间都能推导出推断的类型,但我仍然有点困惑。例如,我知道要获得(.)map uncurry的答案,首先需要派生map uncurry的类型。这是我能做到的 鉴于以下类型导出组合函数的推断类型Haskell:特别是(.)map uncurry,haskell,types,ghci,unification,inferred-type,Haskell,Types,Ghci,Unification,Inferred Type,这里有很多关于导出组合函数的推断类型的线程,但我仍然相当困惑。我发现的所有帖子都没有对如何统一类型给出一般性的解释 我的一本考试学习指南有问题,我很难弄清楚 8) (.)map uncurry的推断类型是什么::\uuuuuuuuuuuuuuuuuuuuuu 我大部分时间都能推导出推断的类型,但我仍然有点困惑。例如,我知道要获得(.)map uncurry的答案,首先需要派生map uncurry的类型。这是我能做到的 鉴于以下类型 map :: (a -> b) -> [a] -
map :: (a -> b) -> [a] -> [b]
uncurry :: (a -> b -> c) -> (a, b) -> c
我用uncurry so统一了map中的函数(a->b)
a = a → b → c
b = (a, b) → c
然后答案是map[a]->[b]的另一半,新的值是a和b so
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是从哪里来的,也不知道它是怎么做的。
我想我真正需要的是对类型统一的解释。统一两种类型的方法是什么?我如何知道两种类型是否不能统一
如果有人能一步一步地解释如何推导(.)map uncurry,我将不胜感激。(.)map uncurry
相当于(.)map uncurry
。map
功能不应用于uncurry
,它被传递到()
。然后,结果(希望可以推断为一个函数)作为参数接收uncurry
至于b1
的来源,别忘了类型变量名并不重要,您可以重命名它们,只要您以类似方式重命名给定类型变量的所有引用。因此:
(.) map uncurry :: (a -> b1 -> b) -> [(a, b1)] -> [b]
相当于:
(.) map uncurry :: (apple -> pear -> plum) -> [(apple, pear)] -> [plum]
(.)map uncurry
相当于(.)map)uncurry
。map
功能不应用于uncurry
,它被传递到()
。然后,结果(希望可以推断为一个函数)作为参数接收uncurry
至于b1
的来源,别忘了类型变量名并不重要,您可以重命名它们,只要您以类似方式重命名给定类型变量的所有引用。因此:
(.) map uncurry :: (a -> b1 -> b) -> [(a, b1)] -> [b]
相当于:
(.) map uncurry :: (apple -> pear -> plum) -> [(apple, pear)] -> [plum]
派生类型签名的关键思想:
请注意,函数应用程序具有最高优先级,并与左侧关联,因此:
->
关联到右侧(.) :: (b -> c) -> (a -> b) -> a -> c
map :: (a -> b) -> [a] -> [b]
uncurry :: (a -> b -> c) -> (a, b) -> c
Hugs> :t (.) map
(map .) :: (a -> b -> c) -> a -> [b] -> [c]
为每个函数指定不重叠的类型名称
(.) :: (b -> c) -> (a -> b) -> a -> c
map :: (a -> b) -> [a] -> [b]
uncurry :: (a -> b -> c) -> (a, b) -> c
Hugs> :t (.) map
(map .) :: (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)
必须与映射的类型统一:
(.) :: ( 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等口译员核实
(.) :: (b -> c) -> (a -> b) -> a -> c
map :: (a -> b) -> [a] -> [b]
uncurry :: (a -> b -> c) -> (a, b) -> c
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])))
将其应用于未经批准的
(.) 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) ++ [] ++ []
按优先顺序排列运算符
(.) map :: (( a -> ( d -> e)) -> (a -> ([d]->[e])))
uncurry :: (f->(g->h)) -> ((f,g) -> h)
使用解释器的:i
命令查找运算符的固定性,并将它们按顺序排列,最高优先:
infixr 9 .
infixr 5 ++
infixr 0 $
优先级最高的运算符
首先被括起来:
(length . (map (.))) $ (repeat id) ++ [] ++ []
然后,++
,它与右侧关联:
(length . (map (.))) $ ((repeat id) ++ ([] ++ []))
而且$
只有一个用法,所以我没有费心把它括起来
如果愿意,可以将++
之类的运算符转换为函数(++)
,但我完全不相信这会对您有所帮助,所以我就不谈了,只要记住第一个参数在左边
从最嵌套的括号开始通常会有所帮助。在派生类型签名时:
在开始之前,请确保您的操作员和功能先例正确无误。
请注意,函数应用程序具有最高优先级a