Haskell 如何手动推断';(.) . (.) . (.)和#x27;?

Haskell 如何手动推断';(.) . (.) . (.)和#x27;?,haskell,types,type-inference,function-composition,combinators,Haskell,Types,Type Inference,Function Composition,Combinators,在爱德华·科米特(Edward Kmett)的演讲中,在幻灯片“点的力量”中,他展示了(.)的类型。(.) . () (a->b)->(c->d->e->a)->c->d->e->b 我可以通过在GHCI中显示其类型来查看它。但我也想知道为什么。我想了解的另一件事是,为什么参数会有规律地从(.)到(.)变化。()和(。(.) . (): 另外,我试图解决()。(。我自己通过扩展(.)的函数定义。()。应用(.)的定义后,我得到: \x y z t -> x ((y z) t) 所以我推断

在爱德华·科米特(Edward Kmett)的演讲中,在幻灯片“点的力量”中,他展示了
(.)的类型。(.) . ()

(a->b)->(c->d->e->a)->c->d->e->b

我可以通过在GHCI中显示其类型来查看它。但我也想知道为什么。我想了解的另一件事是,为什么参数会有规律地从
(.)到
(.)变化。()
(。(.) . ()

另外,我试图解决
()。(。
我自己通过扩展
(.)的函数定义。()
。应用
(.)的定义后,我得到:

\x y z t -> x ((y z) t)
所以我推断出了这些类型:

x :: a -> b
y :: c -> d -> a
z :: c
t :: d
但是我在
(.)上迷路了。(.) . ()
。我不知道这是否是进行手动类型推断的正确方法。

使用函数

instance Functor ((->) r) where
   -- fmap :: (a -> b) ->   f   a  ->   f   b
   --         (a -> b) -> (r -> a) -> (r -> b)
   fmap          p           q         x = p (q x)   -- fmap = (.)
所以你实际上拥有的是
fmap。fmap。fmap

fmap               :: (a -> b) -> f       a   -> f       b
fmap . fmap        :: (a -> b) -> f (g    a)  -> f (g    b)
fmap . fmap . fmap :: (a -> b) -> f (g (h a)) -> f (g (h b))
那是

 (a -> b) -> (c -> (d -> (e -> a))) -> (c -> (d -> (e -> b)))   ~
 (a -> b) -> (c ->  d ->  e -> a)   -> (c ->  d ->  e -> b)     
为什么
fmap。fmap::(a->b)->f(g a)->f(g b)
?因为

(fmap . fmap) foo = fmap (fmap foo)
{-
fmap            :: (a  ->  b) -> f a     -> f b
foo             ::  a  ->  b
fmap foo        ::               f a     -> f b
fmap foo        :: g a -> g b
fmap (fmap foo) ::               f (g a) -> f (g b)
-}
Mechanical类型派生是关于类型变量的替换和一致重命名。请参阅更多信息,例如或。

()。(.) . (.)
分两步减少:首先减少不带括号的点:

((.) . (.) . (.)) f = (.) ((.) ((.) f))
                    = (.) ((.) (f .))
                    = (.) ((f .) .)
                    = ((f .) .) .)
第二,减少剩余的表达式

((f .) .) .) g = ((f .) .) . g
               = \x -> ((f .) .) (g x)
               = \x -> (f .) . g x
               = \x y -> (f .) (g x y)
               = \x y -> f . g x y
               = \x y z -> f (g x y z)
因此,首先使用
n-1
点在括号中组成
n
点。然后将此构造应用于函数
f::a->b
g
,得到
(…(f.))g
其中每个点对应于
g
接收的一个参数-这就是为什么有一个模式:括号中的每个点处理
g
的一个参数,您需要另一个点来将该点与所有先前的参数组合在一起。经过所有简化后,表达式变为

\x1 x2 ... xn -> f (g x1 x2 ... xn)
其类型是显而易见的


一件好事是,如果我们有后缀运算符,我们可以编写(代码在Agda中)


而不是
((f.))。g

您可能会喜欢函数组合的“语义编辑器组合器”视图,在该视图中,我们将
(.)重命名为
result
。中给出的直觉很好,在我看来,它清楚地说明了为什么堆叠
(.
的组合会产生这样的类型。@DanielWagner,感谢链接。我很快就会读的。事实上,我正要用谷歌搜索“语义编辑器combinators”,因为Edward在演讲中提到了很多。谢谢你的漂亮回答。它回答了我的两个问题:)。谢谢!这就是我最初试图实现的目标。如果我能记下两个正确答案,我也会记下你们的答案。
\x1 x2 ... xn -> f (g x1 x2 ... xn)
open import Function renaming (_∘′_ to _∘_) using ()

_% = _∘_

postulate
  a b c d e : Set
  f : a -> b
  g : c -> d -> e -> a

fg : c -> d -> e -> b
fg = f % % ∘ g