【问题标题】:How to manually infer the type of '(.) . (.) . (.)'?如何手动推断 '(.) 的类型。 (.) 。 (.)'?
【发布时间】:2016-04-14 13:12:44
【问题描述】:

在 Edward Kmett 的演讲 Lenses, Folds, and Traversals 中,在幻灯片“The Power is in the Dot”中,他展示了(.) . (.) . (.) 的类型是

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

我可以通过在 GHCI 中显示它的类型来查看它。但我也想知道为什么。我想了解的另一件事是为什么从(.)(.) . (.)(.) . (.) . (.) 的参数定期更改的模式:

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

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

\x y z t -> x ((y z) t)

所以我推断出类型:

x :: a -> b
y :: c -> d -> a
z :: c
t :: d

但是我在(.) . (.) . (.) 上迷路了。而且我不知道这是否是进行手动类型推断的正确方法。

【问题讨论】:

  • 你可能喜欢函数组合的“语义编辑器组合器”视图,我们将(.)重命名为resultthis post 中给出的直觉非常好,在我看来,这很清楚为什么 (.) 的堆叠组合会产生它所做的类型。
  • @DanielWagner,感谢您的链接。我很快就会读到它。实际上,我正要搜索“语义编辑器组合器”,因为 Edward 在演讲中经常提到它。

标签: haskell types type-inference function-composition combinators


【解决方案1】:

有功能,

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)
-}

机械类型派生是关于类型变量的替换和一致重命名。查看更多例如herehere

【讨论】:

  • 感谢您的精彩回答。它回答了我的两个问题:)。
【解决方案2】:

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

((.) . (.) . (.)) 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 -> bg 并得到(...((f .) .) ... .) g,其中每个点对应于g 接收的一个参数——这就是为什么有一个模式:括号中的每个点处理g 的一个参数你需要另一个点来与之前的所有点组成这个点。在所有归约之后,表达式变为

\x1 x2 ... xn -> f (g x1 x2 ... xn)

而且它的类型很明显。


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

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

而不是((f .) .) . g

【讨论】:

  • 谢谢!这就是我最初试图实现的目标。如果我可以标记两个正确答案,我也会将您的答案标记为正确。
猜你喜欢
  • 1970-01-01
  • 2012-12-29
  • 1970-01-01
  • 1970-01-01
  • 2023-02-05
  • 1970-01-01
  • 1970-01-01
  • 2014-11-20
  • 2014-08-27
相关资源
最近更新 更多