了解 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 af (t b)

你能给我解释一下吗?

traverse 是一个类型为 class 的函数,所以遗憾的是这个函数的行为取决于我们选择的 t 是什么。这与 >>=fmap 并无不同。然而,它的行为有规则,就像在那些情况下一样。这些规则应该捕捉 traverse 采用函数 a -> f b 的想法,这是从 ab 的有效转换,并将其提升到整个 "container" 共 a 秒,收集每个局部变换的效果。

例如,如果我们有 Maybe atraverse 的实现将是

 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 af 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
  • 一个充满 as
  • 的函子
  • 我们通过重复应用 f 得到一个充满 b 的仿函数,ala fmap
  • f的所有效果累加起来,所以我们得到f (t b),而不仅仅是t b

不过请记住,traverse 可以以一些奇怪的方式使用。例如,镜头包中充满了使用 traverse 和非常奇怪的函子来达到很好的效果。

作为一个快速测试,你能想出如何使用合法的 traverset 实现 fmap 吗?即

fmapOverkill :: Traversable f => (a -> b) -> (f a -> f b)

headMay

headMay :: Traversable t => t a -> Maybe a

这两个都是可遍历实例同时满足FunctorFoldable!

的结果