Skip to content

Commit

Permalink
Add StreamT#asStream: conversion of StreamT[Id, A] to a lazy Stream[A].
Browse files Browse the repository at this point in the history
  • Loading branch information
TomasMikula committed Mar 15, 2016
1 parent ea85675 commit 3d5a7c7
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 0 deletions.
21 changes: 21 additions & 0 deletions core/src/main/scala/scalaz/StreamT.scala
Expand Up @@ -113,7 +113,28 @@ sealed class StreamT[M[_], A](val step: M[StreamT.Step[A, StreamT[M, A]]]) {
)
}

/**
* **Warning:** Requires evaluation of the whole stream. Depending on
* the monad `M`, the evaluation will happen either immediately, or
* will be deferred until the resulting `Stream` is extracted from the
* returned `M`.
*/
def toStream(implicit M: Monad[M]): M[Stream[A]] = M.map(rev)(_.reverse)

/**
* Converts this `StreamT` to a lazy `Stream`, i.e. without forcing
* evaluation of all elements. Note, however, that at least one element
* of this stream will be evaluated, and depending on the structure of
* this stream, up to two elements might be evaluated.
*/
def asStream(implicit ev: M[Step[A, StreamT[M, A]]] =:= Id[Step[A, StreamT[Id, A]]]): Stream[A] = {
def go(s: StreamT[Id, A]): Stream[A] = s.uncons match {
case None => Stream.empty[A]
case Some((a, s1)) => Stream.cons(a, go(s1))
}

go(StreamT(ev(step)))
}

def foldRight[B](z: => B)(f: (=> A, => B) => B)(implicit M: Monad[M]): M[B] =
M.map(rev) {
Expand Down
24 changes: 24 additions & 0 deletions tests/src/test/scala/scalaz/StreamTTest.scala
Expand Up @@ -13,6 +13,30 @@ object StreamTTest extends SpecLite {
StreamT.fromStream(ass).toStream must_===(ass)
}

"fromStream / asStream" ! forAll {
import Id._
(as: Stream[Int]) =>
StreamT.fromStream[Id, Int](as).asStream must_===(as)
}

"asStream" should {
"be lazy" in {
var highestTouched = 0

val s1 = StreamT.unfold(1)(i => {
highestTouched = math.max(i, highestTouched)
if(i < 100) Some((i, i+1)) else None
})

val s2 = s1.asStream

// test that at most 2 elements were evaluated in the conversion
// (the fact that 2 are actually evaluated is a consequence of
// how Stream.cons and StreamT.unfold are implemented)
highestTouched mustBe_< 3
}
}

"filter all" ! forAll {
(ass: StreamT[Stream, Int]) =>
ass.filter(_ => true) must_===(ass)
Expand Down

0 comments on commit 3d5a7c7

Please sign in to comment.