我怎么能理解“()。 (。)”?(How can I understand “(.) . (.)

2019-07-20 09:30发布

我相信我明白fmap . fmap fmap . fmap的函子,但在功能它现在伤害我的头几个月。

我已经看到了你可以申请的定义(.)(.) . (.) (.) . (.)但我已经忘了如何做到这一点。
当我自己试试吧让它一直都错了:

(.) f g = \x -> f (g x)
(.) (.) (.) = \x -> (.) ((.) x)
\x f -> (.) ((.) x) f
\x f y  -> (((.)(f y)) x)
\x f y g-> (((.)(f y) g) x)
\x f y g-> ((f (g y)) x)
\x f y g-> ((f (g y)) x):: t2 -> (t1 -> t2 -> t) -> t3 -> (t3 -> t1) -> t

如果“只应用定义”是这样做的唯一途径,怎么有人想出了(.) . (.) (.) . (.)
必须有一些更深层次的理解和直觉我失踪。

Answer 1:

你也可以用你的理解fmap . fmap fmap . fmap

如果你有两个Functor小号foobar ,然后

fmap . fmap :: (a -> b) -> foo (bar a) -> foo (bar b)

fmap . fmap fmap . fmap取一个函数和用于两个的组合物产生的感应功能Functor秒。

现在,对于任何类型的t(->) t是一个Functor ,并且fmapFunctor(.)

所以(.) . (.) (.) . (.)fmap . fmap fmap . fmap为的情况下,其中两个Functor s为(->) s(->) t ,因此

(.) . (.) :: (a -> b) -> ((->) s) ((->) t a) -> ((->) s) ((->) t b)
          =  (a -> b) -> (s -> (t -> a))     -> (s -> (t -> b))
          =  (a -> b) -> (s -> t -> a)       -> (s -> t -> b)

它“组成”的函数f :: a -> b具有两个参数,一个函数g :: s -> t -> a

((.) . (.)) f g = \x y -> f (g x y)

这一观点也清楚地表明,以及如何,图案延伸至采取更多参数的函数,

(.) . (.) . (.) :: (a -> b) -> (s -> t -> u -> a) -> (s -> t -> u -> b)

等等



Answer 2:

未来与(.) . (.) (.) . (.)其实是非常简单的,它的背后它做什么,这是很棘手的理解直觉。

(.)重写表达到“管”式计算(想象一下如果让你很远|在外壳)。 然而,它变得尴尬,一旦你尝试编写一个函数,多个参数用一个函数,只需要一个使用。 作为一个例子,让我们的定义concatMap

concatMap :: (a -> [b]) -> [a] -> [b]
concatMap f xs = concat (map f xs)

摆脱xs是一个标准的操作:

concatMap f = concat . map f

然而,有摆脱没有“好”的方式f 。 这是由以下事实造成的,这map有两个参数,我们想申请concat其最终结果。

当然,你可以涂抹一些pointfree技巧和逃脱刚(.)

concatMap f = (.) concat (map f)
concatMap f = (.) concat . map $ f
concatMap = (.) concat . map
concatMap = (concat .) . map

但很可惜,这段代码的可读性大多了。 相反,我们引入一个新的组合子,这不正是我们所需要的:应用第二功能,第一个最终结果

-- .: is fairly standard name for this combinator
(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
(f .: g) x y = f (g x y)

concatMap = concat .: map

好吧,这就是它的动机。 让我们到pointfree业务。

(.:) = \f g x y -> f (g x y)
     = \f g x y -> f ((g x) y)
     = \f g x y -> f . g x $ y
     = \f g x   -> f . g x

现在,来这里的有趣的部分。 这是另一种的pointfree招数通常有助于当你卡住:我们重写. 成其前缀的形式,并尝试从那里继续。

     = \f g x   -> (.) f (g x)
     = \f g x   -> (.) f . g $ x
     = \f g     -> (.) f . g
     = \f g     -> (.) ((.) f) g
     = \f       -> (.) ((.) f)
     = \f       -> (.) . (.) $ f
     =             (.) . (.)

至于直觉,有这非常好的文章 ,你应该阅读。 我会转述有关的部分(.)

让我们再想想我们的组合子应该做的:它应该适用f到的结果结果 g (我一直在使用的最终结果在部分之前故意的,真的是你得到什么,当你完全适用-模统一类型变量与其他功能型-在g功能, 导致这里只是应用gx一些x )。

这是什么意思为我们申请f到的结果 g ? 那么,一旦我们应用g于一定的价值,我们将结果和应用f它。 听起来很熟悉:这就是(.)一样。

result :: (b -> c) -> ((a -> b) -> (a -> c))
result = (.)

现在,事实证明,这些组合子组成(我们话)仅仅是一个功能组成,即:

(.:) = result . result -- the result of result


Answer 3:

当你介绍你的解决方案发散y 。 它应该是

\x f y -> ((.) ((.) x) f) y     :: (c -> d) -> (a -> b -> c) -> a -> b -> d
\x f y z -> ((.) ((.) x) f) y z :: (c -> d) -> (a -> b -> c) -> a -> b -> d
\x f y z -> ((.) x (f y)) z     :: (c -> d) -> (a -> b -> c) -> a -> b -> d
-- Or alternately:
\x f y z -> (x . f y) z         :: (c -> d) -> (a -> b -> c) -> a -> b -> d
\x f y z -> (x (f y z))         :: (c -> d) -> (a -> b -> c) -> a -> b -> d

其中原始类型签名匹配: (.) . (.) :: (c -> d) -> (a -> b -> c) -> a -> b -> d (.) . (.) :: (c -> d) -> (a -> b -> c) -> a -> b -> d

(这是最容易做的扩展在ghci中,在那里你可以检查每一步:t expression

编辑:

更深层次的intution是这样的:

(.)被简单地定义为

\f g -> \x -> f (g x)

我们可以简化为

\f g x -> f (g x)

所以,当你为它供给两个参数,这是令行禁止,仍然需要另一种说法来解决。 每次使用(.)以2个参数,创建自己的“需求”的另一个理由。

(.) . (.) (.) . (.)当然只是(.) (.) (.)所以让我们展开:

(\f0 g0 x0 -> f0 (g0 x0)) (\f1 g1 x1 -> f1 (g1 x1)) (\f2 g2 x2 -> f2 (g2 x2))

我们可以对β-减少f0g0 (但我们没有一个x0 !):

\x0 -> (\f1 g1 x1 -> f1 (g1 x1)) ((\f2 g2 x2 -> f2 (g2 x2)) x0) 

替换第二表达式f1 ...

\x0 -> \g1 x1 -> ((\f2 g2 x2 -> f2 (g2 x2)) x0) (g1 x1) 

现在,“翻转回来”! (β-减少上f2 ):
这是有趣的步骤- x0代入f2 -这意味着x ,这本来是数据,是不是函数。
(.) . (.) (.) . (.)提供- “需要”为额外的参数。

\x0 -> \g1 x1 -> (\g2 x2 -> x0 (g2 x2)) (g1 x1) 

这是开始看起来正常...让我们的β-减少上一次(上g2 ):

\x0 -> \g1 x1 -> (\x2 -> x0 ((g1 x1) x2))

因此,我们只剩下简单地

\x0 g1 x1 x2 -> x0 ((g1 x1) x2)

,这里的参数是很好的仍然井然有序。



Answer 4:

所以,这是我所得到的,当我做了稍微的增量扩张

(.) f g   = \x -> f (g x)
(.) . g   = \x -> (.) (g x)
          = \x -> \y -> (.) (g x) y
          = \x -> \y -> \z -> (g x) (y z)
          = \x y z -> (g x) (y z)
(.) . (.) = \x y z -> ((.) x) (y z)
          = \x y z -> \k -> x (y z k)
          = \x y z k -> x (y z k)

其中,根据ghci中具有正确的类型

Prelude> :t (.) . (.)
(.) . (.) :: (b -> c) -> (a -> a1 -> b) -> a -> a1 -> c
Prelude> :t \x y z k -> x (y z k)
\x y z k -> x (y z k)
  :: (t1 -> t) -> (t2 -> t3 -> t1) -> t2 -> t3 -> t
Prelude> 

虽然我不知道这个组合子的起源,很可能是有人在组合逻辑,在那里你与组合程序严格工作而开发的,因此使用更方便lambda表达式,你无法定义的东西。 可能有一些直觉与盘算这些东西去,但我还没有找到它。 最有可能的,你会如果你不得不这样做足够的发展直觉的一些水平。



Answer 5:

这是最简单的书写公式 , 组合子式的 ,而不是拉姆达表达式: abc = (\x -> ... body ...)等同于abcx = ... body ... ,反之亦然,前提是x不出现之间{a,b,c} 所以,

-- _B = (.)  

_B f g x = f (g x)
_B _B _B f g x y = _B (_B f) g x y
                 = (_B f) (g x) y
                 = _B f (g x) y
                 = f ((g x) y)
                 = f (g x y)

如果,给你发现这个f (gxy)你想把它转换成组合子形式 (摆脱所有的括号和可变重复)。 然后,你将对应于组合子的定义,希望追查这个推导向后模式。 这是少得多的机械/自动不过。



文章来源: How can I understand “(.) . (.)”?