了解 haskell 中遍历函数的签名
Understanding signature of function traverse in haskell
traverse :: Applicative f => (a -> f b) -> t a -> f (t b)
嗨,
有很多我无法理解签名的功能。当然我明白遍历有两个参数,第一个是函数。然而,
(a -> f b)
是什么意思?我能看懂(a -> b)
.
同样,t a
、f (t b)
你能给我解释一下吗?
traverse
是一个类型为 class 的函数,所以遗憾的是这个函数的行为取决于我们选择的 t
是什么。这与 >>=
或 fmap
并无不同。然而,它的行为有规则,就像在那些情况下一样。这些规则应该捕捉 traverse
采用函数 a -> f b
的想法,这是从 a
到 b
的有效转换,并将其提升到整个 "container" 共 a
秒,收集每个局部变换的效果。
例如,如果我们有 Maybe a
,traverse
的实现将是
traverse f (Just a) = Just <$> f a
traverse f Nothing = pure Nothing
对于列表
traverse f [a1, a2, ...] = (:) <$> f a1 <*> ((:) <$> f a2 <*> ...))
请注意我们如何利用 "effect" f
不仅是函子而且是可应用的这一事实,因此我们可以进行两次 f-ful 计算,f a
和 f b
并将它们粉碎在一起得到 f (a, b)
。现在我们想提出一些法则来解释 all 遍历可以做的是将 f
应用于元素并在收集元素的同时构建原始的 t a
备份对外界的影响。我们说
traverse Identity = Identity -- We don't lose elements
t . traverse f = traverse (t . f) -- For nicely composing t
traverse (Compose . fmap g . f) = Compose . fmap (traverse g) . traverse f
现在这看起来很复杂,但它所做的只是澄清 "Basically walks around and applies the local transformation" 的含义。所有这一切归结为,虽然您不能只阅读签名来理解 traverse
的作用,但对签名的正确直觉是
- 我们得到了一个局部有效的函数
f :: a -> f b
- 一个充满
a
s 的函子
- 我们通过重复应用
f
得到一个充满 b
的仿函数,ala fmap
f
的所有效果累加起来,所以我们得到f (t b)
,而不仅仅是t b
。
不过请记住,traverse
可以以一些奇怪的方式使用。例如,镜头包中充满了使用 traverse
和非常奇怪的函子来达到很好的效果。
作为一个快速测试,你能想出如何使用合法的 traverse
为 t
实现 fmap
吗?即
fmapOverkill :: Traversable f => (a -> b) -> (f a -> f b)
或headMay
headMay :: Traversable t => t a -> Maybe a
这两个都是可遍历实例同时满足Functor
和Foldable
!
的结果
traverse :: Applicative f => (a -> f b) -> t a -> f (t b)
嗨,
有很多我无法理解签名的功能。当然我明白遍历有两个参数,第一个是函数。然而,
(a -> f b)
是什么意思?我能看懂(a -> b)
.
同样,t a
、f (t b)
你能给我解释一下吗?
traverse
是一个类型为 class 的函数,所以遗憾的是这个函数的行为取决于我们选择的 t
是什么。这与 >>=
或 fmap
并无不同。然而,它的行为有规则,就像在那些情况下一样。这些规则应该捕捉 traverse
采用函数 a -> f b
的想法,这是从 a
到 b
的有效转换,并将其提升到整个 "container" 共 a
秒,收集每个局部变换的效果。
例如,如果我们有 Maybe a
,traverse
的实现将是
traverse f (Just a) = Just <$> f a
traverse f Nothing = pure Nothing
对于列表
traverse f [a1, a2, ...] = (:) <$> f a1 <*> ((:) <$> f a2 <*> ...))
请注意我们如何利用 "effect" f
不仅是函子而且是可应用的这一事实,因此我们可以进行两次 f-ful 计算,f a
和 f b
并将它们粉碎在一起得到 f (a, b)
。现在我们想提出一些法则来解释 all 遍历可以做的是将 f
应用于元素并在收集元素的同时构建原始的 t a
备份对外界的影响。我们说
traverse Identity = Identity -- We don't lose elements
t . traverse f = traverse (t . f) -- For nicely composing t
traverse (Compose . fmap g . f) = Compose . fmap (traverse g) . traverse f
现在这看起来很复杂,但它所做的只是澄清 "Basically walks around and applies the local transformation" 的含义。所有这一切归结为,虽然您不能只阅读签名来理解 traverse
的作用,但对签名的正确直觉是
- 我们得到了一个局部有效的函数
f :: a -> f b
- 一个充满
a
s 的函子
- 我们通过重复应用
f
得到一个充满b
的仿函数,alafmap
f
的所有效果累加起来,所以我们得到f (t b)
,而不仅仅是t b
。
不过请记住,traverse
可以以一些奇怪的方式使用。例如,镜头包中充满了使用 traverse
和非常奇怪的函子来达到很好的效果。
作为一个快速测试,你能想出如何使用合法的 traverse
为 t
实现 fmap
吗?即
fmapOverkill :: Traversable f => (a -> b) -> (f a -> f b)
或headMay
headMay :: Traversable t => t a -> Maybe a
这两个都是可遍历实例同时满足Functor
和Foldable
!