使用包含在选项中的流

Working with Streams wrapped in Options

我正在处理一些嵌套的 Streams 并且想对它们使用 for comprehension 语法:

def handleNestedStream(as : Stream[A]) : Stream[(A, B)] = {
    a <- as
    b <- makeBs(a)
} yield (a, b)

但是,makeBs 函数 return 是一个 Option[Stream[B]]。我希望 Option 自动展开。此外,如果 makeBs 失败,我希望整个函数 return None 。所以新函数看起来像这样:

def makeBs(a : A) : Option[Stream[B]] = { ... }

def handleNestedStream(as : Stream[A]) : Option[Stream[(A, B)]] = {
    a <- as
    b <- makeBs(a)
} yield (a, b)

唯一的变化是函数的类型。

我怎样才能完成这样的事情?来自 cats 的 StreamingT 或来自 scalaz 的 StreamT 可以帮助解决这个问题吗?

有些类型是灵活的。 makeBs 可以改为 return Stream[Option[B]] 而不是 Option[Stream[B]] 如果这样可以使事情更简单。

我需要使用 scala 标准库 Stream 类型。

让我们想象一下实现

import scalaz._
import std.option._
import syntax.std.option._

type StreamO[X] = StreamT[Option,X]

def makeBs(a : A) : StreamO[B] = ???

def handleNestedStream(as : Stream[A]) : StreamO[(A, B)] = for {
  a <- StreamT fromStream as.some
  b <- makeBs(a)
} yield (a, b)

假设现在

import syntax.monad._
type A = Int
type B = String
def makeBs(a : A) = for (x <- a.point[StreamO] if x % 2 == 1) yield x.toString * x

handleNestedStream(1 to 5 toStream).toStream

将被评估为

Some(Stream((1,1), (3,333), (5,55555)))

另一种方法是使用 traverseM from scalaz:

import scalaz._, Scalaz._

def handleNestedStream(as : Stream[A]) : Option[Stream[(A, B)]] = 
  as.traverseM(a => makeBs(a).map(_.map(a ->)))

traverseM 的主要签名是 traverseM(fa: F[A])(f: A => G[F[B]]): G[F[B]]F 应该有 TraverseBind 的实例,G 应该有Applicative 的实例)。在这种情况下,FStreamGOption,签名中的 B 是您示例中的 (A, B)

所以如果你在 Stream[A] 上调用 traverseM,并想返回 Option[Stream[(A, B)]],你应该给它传递一个函数 A => Option[Stream[(A, B)]] – 这自然是 makeBs,然后是构成 (A, B) 对的深度图。

后缀为M的函数(filterMtraverseMfoldLeftM等)通常在你想组合几个不同的上下文时非常有用,但是没有 monad 转换器的样板。