0

I am working with some nested Streams and would like to use the for comprehension syntax with them:

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

However, the makeBs function returns an Option[Stream[B]]. I would like the Option to be unwrapped automatically. In addition, I would like the entire function to return None if makeBs fails. So the new function would look something like this:

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

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

The only change is the type of the function.

How can I accomplish something like this? Can StreamingT from cats or StreamT from scalaz help here?

Some of the types are flexible. makeBs can be made to return Stream[Option[B]] instead of Option[Stream[B]] if that would make things simpler.

I need to use the scala standard lib Stream type.

mushroom
  • 6,201
  • 5
  • 36
  • 63

2 Answers2

2

Another way to do this is to use traverseM from scalaz:

import scalaz._, Scalaz._

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

The principal signature of traverseM is traverseM(fa: F[A])(f: A => G[F[B]]): G[F[B]] (F should have instances of Traverse and Bind, and G should have an instance of Applicative). In this case F is Stream, G is Option, and B in the signature is (A, B) from your example.

So if you call traverseM on Stream[A], and want to get back Option[Stream[(A, B)]], you should pass it a function A => Option[Stream[(A, B)]] – this is naturally makeBs, followed by a deep map that makes (A, B) pairs.

The functions with suffix M (filterM, traverseM, foldLeftM, etc.) are generally quite useful when you want to combine several different contexts, but without the boilerplate of monad transformers.

Kolmar
  • 14,086
  • 1
  • 22
  • 25
1

Let's imagine that implementation

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)

Suppose now

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

will be evaluated as

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

Odomontois
  • 15,918
  • 2
  • 36
  • 71