我相信我明白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
小号foo
和bar
,然后
fmap . fmap :: (a -> b) -> foo (bar a) -> foo (bar b)
fmap . fmap
fmap . fmap
取一个函数和用于两个的组合物产生的感应功能Functor
秒。
现在,对于任何类型的t
, (->) t
是一个Functor
,并且fmap
该Functor
是(.)
所以(.) . (.)
(.) . (.)
是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))
我们可以对β-减少f0
和g0
(但我们没有一个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 “(.) . (.)”?