重复 fmap 的乐趣

发布于 2024-12-25 02:55:50 字数 788 浏览 4 评论 0原文

我在玩仿函数时,注意到一些有趣的事情:

简单地说,id 可以在类型 (a -> b) ->; 上实例化。一个-> b.

通过列表函子,我们有 fmap :: (a -> b) -> [一]-> [b],与map相同。

对于 ((->) r) 函子(来自 Control.Monad.Instances),fmap 是函数组合,因此我们可以实例化 fmap fmap fmap :: (a -> b) -> [[a]]-> [[b]],相当于(map .map)

有趣的是,fmap 八次给我们(map .map .map)

那么我们有

0: id = id
1: fmap = map
3: fmap fmap fmap = (map . map)
8: fmap fmap fmap fmap fmap fmap fmap fmap = (map . map . map)

这种模式会继续吗?为什么/为什么不呢?是否有一个公式可以计算我需要多少个 fmap 将函数映射到 n 次嵌套列表上?

我尝试编写一个测试脚本来搜索 n = 4 情况的解决方案,但 GHC 在大约 40 fmap 时开始消耗太多内存。

I was playing around with functors, and I noticed something interesting:

Trivially, id can be instantiated at the type (a -> b) -> a -> b.

With the list functor we have fmap :: (a -> b) -> [a] -> [b], which is the same as map.

In the case of the ((->) r) functor (from Control.Monad.Instances), fmap is function composition, so we can instantiate fmap fmap fmap :: (a -> b) -> [[a]] -> [[b]], which is equivalent to (map . map).

Interestingly, fmap eight times gives us (map . map . map)!

So we have

0: id = id
1: fmap = map
3: fmap fmap fmap = (map . map)
8: fmap fmap fmap fmap fmap fmap fmap fmap = (map . map . map)

Does this pattern continue? Why/why not? Is there a formula for how many fmaps I need to map a function over an n-times nested list?

I tried writing a test script to search for a solution to the n = 4 case, but GHC starts eating too much memory at around 40 fmaps.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

离不开的别离 2025-01-01 02:55:50

我无法解释原因,但这是循环的证明:

假设 k >= 2fmap^(4k) :: (a -> b) -> F1 F2 F3 a-> F1 F2 F3 b,其中 Fx 代表未知/任意 Functorfmap^n 代表应用于 n-1 fmapfmap,而不是 n >-折叠迭代。感应的启动可以通过手动或查询 ghci 来验证。

fmap^(4k+1) = fmap^(4k) fmap
fmap :: (x -> y) -> F4 x -> F4 y

与 -> 统一b 产生 a = x -> y, b = F4 x -> F4 y,所以

fmap^(4k+1) :: F1 F2 F3 (x -> y) -> F1 F2 F3 (F4 x -> F4 y)

现在,对于 fmap^(4k+2),我们必须将 F1 F2 F3 (x -> y)( a->b)-> F5a-> F5 b
因此 F1 = (->) (a -> b)F2 F3 (x -> y) 必须与 F5 a -> 统一; F5 b
因此F2 = (->) (F5 a)F3 (x -> y) = F5 b,即F5 = F3和 b = x -> y 。结果是

fmap^(4k+2) :: F1 F2 F3 (F4 x -> F4 y)
             = (a -> (x -> y)) -> F3 a -> F3 (F4 x -> F4 y)

对于fmap^(4k+3),我们必须统一a ->; (x -> y) 与 (m -> n) -> F6米-> F6 n),给出a = m -> n,
x = F6 my = F6 n,所以

fmap^(4k+3) :: F3 a -> F3 (F4 x -> F4 y)
             = F3 (m -> n) -> F3 (F4 F6 m -> F4 F6 n)

最后,我们必须将 F3 (m -> n)( a->b)-> F7a->; F7 b,因此 F3 = (->) (a -> b)m = F7 an = F7 b< /code>,因此

fmap^(4k+4) :: F3 (F4 F6 m -> F4 F6 n)
             = (a -> b) -> (F4 F6 F7 a -> F4 F6 F7 b)

循环完成。当然,结果是通过查询 ghci 得出的,但也许这个推导可以揭示它的工作原理。

I can't explain why, but here's the proof of the cycle:

Assume k >= 2 and fmap^(4k) :: (a -> b) -> F1 F2 F3 a -> F1 F2 F3 b, where Fx stands for an unknown/arbitrary Functor. fmap^n stands for fmap applied to n-1 fmaps, not n-fold iteration. The induction's start can be verified by hand or querying ghci.

fmap^(4k+1) = fmap^(4k) fmap
fmap :: (x -> y) -> F4 x -> F4 y

unification with a -> b yields a = x -> y, b = F4 x -> F4 y, so

fmap^(4k+1) :: F1 F2 F3 (x -> y) -> F1 F2 F3 (F4 x -> F4 y)

Now, for fmap^(4k+2) we must unify F1 F2 F3 (x -> y) with (a -> b) -> F5 a -> F5 b.
Thus F1 = (->) (a -> b) and F2 F3 (x -> y) must be unified with F5 a -> F5 b.
Hence F2 = (->) (F5 a) and F3 (x -> y) = F5 b, i.e. F5 = F3 and b = x -> y. The result is

fmap^(4k+2) :: F1 F2 F3 (F4 x -> F4 y)
             = (a -> (x -> y)) -> F3 a -> F3 (F4 x -> F4 y)

For fmap^(4k+3), we must unify a -> (x -> y) with (m -> n) -> F6 m -> F6 n), giving a = m -> n,
x = F6 m and y = F6 n, so

fmap^(4k+3) :: F3 a -> F3 (F4 x -> F4 y)
             = F3 (m -> n) -> F3 (F4 F6 m -> F4 F6 n)

Finally, we must unify F3 (m -> n) with (a -> b) -> F7 a -> F7 b, so F3 = (->) (a -> b), m = F7 a and n = F7 b, therefore

fmap^(4k+4) :: F3 (F4 F6 m -> F4 F6 n)
             = (a -> b) -> (F4 F6 F7 a -> F4 F6 F7 b)

and the cycle is complete. Of course the result follows from querying ghci, but maybe the derivation sheds some light on how it works.

清眉祭 2025-01-01 02:55:50

我会给出一个稍微简单一点的答案:mapfmap的特化,(.)也是 fmap 的专业化。所以,通过替换,你就得到了你发现的身份!

如果您有兴趣进一步了解,Bartosz Milewski 有一篇不错的文章 使用米田引理来明确为什么函数组合是一个 monad。

I'll give a slightly simpler answer: map is a specialization of fmap and (.) is also a specialization of fmap. So, by substitution, you get the identity you discovered!

If you're interested in going further, Bartosz Milewski has a nice writeup that uses the Yoneda Lemma to make explicit why function composition is a monad.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文