【问题标题】:Stream to be an instance of traversableStream 成为可遍历的实例
【发布时间】:2018-07-11 04:04:58
【问题描述】:

vector-0.1 包有一个相当高效的Stream 实现(Data.Vector.Stream):

data Step s a = Yield a s
              | Skip    s
              | Done

-- | The type of fusible streams
data Stream a = forall s. Stream (s -> Step s a) s Size

vector 的后期版本将此扩展为单子版本 Data.Vector.Fusion.Stream.Monadic,但为简单起见,我们使用旧的非单子版本。

Stream 很自然地是FunctorFoldable 的一个实例:

instance Foldable Stream where
    foldMap f s = foldl' (\a b -> a <> (f b)) mempty s

作为一个流,它也应该是Traversable 的一个实例,不是吗?至少乍一看,它看起来很容易。我们需要一个

sequenceA :: Applicative f => Stream (f a) -> f (Stream a)

开头是

sequenceA (Stream step s) = Stream <$> step' <*> (pure s) where
    step' = ...

&lt;*&gt; 是从Stream 下“拉出”应用程序f 的唯一方法。现在,step' 应该是

step' :: f (s -> Step s a)

我们有一个

step :: s -> Step s (f a)

我只知道fApplicative(和Functor)。但是&lt;*&gt; '拉入'f (&lt;*&gt; :: f(a-&gt;b)-&gt;(f a-&gt;f b)) 而在这里我需要完全相反的,一个co-&lt;*&gt; 可以这么说。

拥有Traversable 实例对我的任何努力都不是至关重要的,从性能的角度来看,它甚至可能不是可取的。但令我烦恼的是,我并没有真正理解为什么Stream 不会是Traversable。使Stream 成为Traversable 缺少什么结构元素?

【问题讨论】:

    标签: haskell stream applicative traversable


    【解决方案1】:

    这是 Traversable,但不是很有趣。因为它允许fromListtoList,所以我们有

    sequenceA = fmap fromList . sequenceA . toList
    

    你真的不能做任何更有趣的事情:你有一个 Stream (f a) 并希望生成 f (Stream a) 代替。由于您的 Stream 与列表同构,因此要将 f 中的效果带到外部级别,您必须遍历流中的每个项目,合并每个项目的效果,最后重建一个流,该流将嵌入应用程序中的对象鹦鹉化结构,与您看到的顺序相同。

    如果您愿意,您可以使用 Stream 模块中的其他函数自己重新实现它,但它的作用基本相同。特别是,它同样懒惰。一种方法是:

    sequenceA (Stream step init) = case step init of
      Yield x s -> cons <$> x <*> sequenceA (Stream step s)
      Skip s -> sequenceA $ Stream step s
      Done -> pure (Stream (const Done) ())
    

    如您所见,与您的尝试的最大不同之处在于您应该映射到 Yield 案例中的 x 值 - 这是合并其效果的唯一方法,因为正如您所指出的,您无法提取值来自f a,仅将其他值推入一个。令人高兴的是,这毕竟是您想要做的!在到达结构的有趣部分之前,您无法对任何内容进行 fmap,这意味着首先调用 step s 以获取其效果。

    您根本不需要pure s,因为您正在构建一个新的流,它具有一种新的内部状态,与您使用的流无关。而且您已经有办法利用范围内的s 值:使用它调用step

    一旦您已经编写了几个使用流的函数(我通过手动实现 Foldable 和 Functor 发现了这一点),这种方法就相当自然了。使用 Stream 的唯一可能方法是在 step s 上进行大小写匹配,如果你从它开始,你就会避开分散你注意力的红鲱鱼。

    【讨论】:

    • @amalloy 给出的两条见解让我很清楚:(1)必须评估所有效果,(2)以正确的顺序;因此需要递归。这可能是大多数(所有?)可遍历的情况。
    • @mcmayer 当然不是所有可遍历的。考虑MaybesequenceA Nothing = pure Nothing; sequenceA (Just x) = Just &lt;$&gt; x。身份有一个同样无聊的例子。您可能会说递归对于所有结构为递归的可遍历是必要的,尽管我认为这是一个观点问题。当然,您可以对类型使用 catamorphism - 这是否相同?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-08-17
    • 2021-01-29
    • 2023-01-25
    • 1970-01-01
    • 1970-01-01
    • 2016-11-01
    • 2010-10-14
    相关资源
    最近更新 更多