10
class (Functor t, Foldable t) => Traversable t where

    traverse :: Applicative f => (a -> f b) -> t a -> f (t b)
    traverse g = sequenceA . fmap g

    sequenceA :: Applicative f => t (f a) -> f (t a)
    sequenceA = traverse id

如何使用它同时继承和Traversable的事实?FoldableFunctor

t作为可遍历类型意味着t也是函子类型和可折叠类型。

我看到这t是一个函子类型的事实,即fmap,用于traverse.

t可折叠类型是否在某处使用?

使用折叠式traverse的事实吗?t

使用哪个事实sequenceAt作为函子类型,t作为可折叠类型,或两者兼而有之?

我们可以定义一个类,它是only 的子类,并且Functor具有以相同方式定义的函数吗?traversesequenceA

谢谢。

4

3 回答 3

13

Foldable实例未使用。不过,要求 是可以的Foldable,因为如果我们可以traverse做某事,那么我们就可以foldMap

foldMapDefault :: (Traversable t, Monoid m) => (a -> m) -> t a -> m
foldMapDefault f = fst . traverse (\a -> (f a, ()))

这里的基本思想是使用标准 writer monad;因为编写器的绑定操作用于mappend组合“写入”部分——这里是f a值——traversemappend正确的东西组合在一起。(它也会建立一个t ()我们实际上并不关心的;扔掉那部分是 的工作fst。)

为了简单和自包含,我使用了 writer monad,但真正的实现使用了稍微令人费解的Const应用程序来避免构建(然后丢弃)无趣的t ()值。您可以在foldMapDefault 此处查看文档及其实现。

于 2019-07-30T04:12:39.003 回答
4

这里开始,类Traversable是 aFunctor和 a Foldable,并且必须满足以下定律:

在此处Foldable查看更多信息。这意味着它可以折叠(foldMap、foldr、foldl...)

traverse函数必须满足以下定律:

  • 自然性:

    吨。traverse f = traverse (t . f) 对于每个应用变换 t

  • 身份

    遍历身份 = 身份

  • 作品

    遍历 (Compose . fmap g . f) = Compose . fmap(遍历 g)。遍历 f

和序列A:

  • 自然性

    吨。序列A = 序列A。fmap t 用于每个应用变换 t

  • 身份

    序列A。fmap 身份 = 身份

  • 作品

    序列A。fmap 撰写 = 撰写。fmap 序列A . 序列A

sequenceA 使用哪个事实:t 是 Functor 类型,t 是 Foldable 类型,还是两者兼而有之?

Traversable,正如它的定义所说(以及上面引用的法律):

class (Functor t, Foldable t) => Traversable t where

既是 aFunctor又是可折叠的,根据它必须遵守的法则,它不仅是 a Functor,比 a 更具体Functor(但仍然是 Functor,因为满足 Functor 的法则并且可以使用其类型类接口的功能),并且甚至比 更具体Foldable,因此更强大,更不通用,具有更多约束。

事实是什么?Traversable定义,但为什么选择这两个的设计师?因为很有用,正如您在@Daniel Wagner 的回答中看到的那样。其他示例:

instance Traversable [] where
    traverse f = List.foldr cons_f (pure [])
      where cons_f x ys = liftA2 (:) (f x) ys

这个使用foldr

instance Foldable [] where
    elem    = List.elem
    foldl   = List.foldl
    foldl'  = List.foldl'
    foldl1  = List.foldl1
    foldr   = List.foldr
    foldr1  = List.foldr1
    length  = List.length
    maximum = List.maximum
    minimum = List.minimum
    null    = List.null
    product = List.product
    sum     = List.sum
    toList  = id

所以,Traverseis aFunctor和 aFoldable所以你可以在需要的时候使用它的接口的功能。Traversable(在示例中,这只是一个示例,而不是设计者选择使用Functorand定义的理由Foldable),因为它是有用的。

于 2019-07-30T04:16:48.787 回答
4

一般来说,子类化有两个主要原因:

  1. 您需要该类来使您的定义工作,或者至少它使实现更加清晰,以至于将其排除在外没有任何意义。对于Functor.

  2. 您可以从您已经拥有的主要定义部分免费派生另一个类,因此您也可以声明它。对于Foldable.

于 2019-07-30T12:22:45.367 回答