Skip to content

Commit

Permalink
Merge pull request #279 from ThoughtWorksInc/lazylist
Browse files Browse the repository at this point in the history
Make Yield lazy in LazyList domain (fix #278)
  • Loading branch information
Atry authored Jul 23, 2019
2 parents 20f392a + dc147d1 commit a55dd6f
Show file tree
Hide file tree
Showing 2 changed files with 275 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import scala.language.higherKinds
*/
final case class Yield[Element](element: Element) extends AnyVal with Keyword[Yield[Element], Unit]

private[keywords] trait LowPriorityYield2 {
private[keywords] trait LowPriorityYield3 {

def apply[A](elements: A*) = {
From(elements)
Expand Down Expand Up @@ -66,7 +66,7 @@ private[keywords] object YieldScalaVersions {
@enableMembersIf(scala.util.Properties.versionNumberString.matches("""^2\.1(1|2)\..*$"""))
object Scala211Or212 {

trait LowPriorityYield1 extends LowPriorityYield2 {
trait LowPriorityYield1 extends LowPriorityYield3 {

implicit def seqViewYieldFromDsl[A, FromCollection <: Traversable[A], Coll1, Coll2](
implicit canBuildFrom: CanBuildFrom[SeqView[A, Coll1], A, SeqView[A, Coll2]])
Expand Down Expand Up @@ -112,7 +112,7 @@ private[keywords] object YieldScalaVersions {
@enableMembersIf(scala.util.Properties.versionNumberString.matches("""^2\.13\..*$"""))
object Scala213 {

trait LowPriorityYield1 extends LowPriorityYield2 {
trait LowPriorityYield2 extends LowPriorityYield3 {

implicit def viewYieldFromDsl[A, FromCollection <: View.SomeIterableOps[A]]
: Dsl[From[FromCollection], View[A], Unit] =
Expand Down Expand Up @@ -144,7 +144,7 @@ private[keywords] object YieldScalaVersions {
}
}

trait LowPriorityYield0 extends LowPriorityYield1 {
trait LowPriorityYield1 extends LowPriorityYield2 {
implicit def seqYieldFromDsl[A,
FromCollection <: View.SomeIterableOps[A],
Collection[X] <: SeqOps[X, Collection, Collection[X]]]
Expand All @@ -162,7 +162,32 @@ private[keywords] object YieldScalaVersions {
keyword.element +: generateTail(())
}
}
}

trait LowPriorityYield0 extends LowPriorityYield1 {

implicit def lazyListYieldFromDsl[A, FromCollection <: Iterable[A]]
: Dsl[From[FromCollection], LazyList[A], Unit] =
new Dsl[From[FromCollection], LazyList[A], Unit] {
def cpsApply(keyword: From[FromCollection], generateTail: Unit => LazyList[A]): LazyList[A] = {
keyword.elements.to(LazyList) #::: generateTail(())
}
}

implicit def lazyListYieldDsl[Element, That >: Element]: Dsl[Yield[Element], LazyList[That], Unit] =
new Dsl[Yield[Element], LazyList[That], Unit] {
def cpsApply(keyword: Yield[Element], generateTail: Unit => LazyList[That]): LazyList[That] = {
keyword.element #:: generateTail(())
}
}

implicit def futureLazyListYieldDsl[Element, That >: Element]: Dsl[Yield[Element], LazyList[Future[That]], Unit] =
new Dsl[Yield[Element], LazyList[Future[That]], Unit] {
def cpsApply(keyword: Yield[Element],
generateTail: Unit => LazyList[Future[That]]): LazyList[Future[That]] = {
Future.successful(keyword.element) #:: generateTail(())
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
package com.thoughtworks.dsl.keywords

import com.thoughtworks.dsl.Dsl.!!
import com.thoughtworks.enableMembersIf
import org.scalatest.{FreeSpec, Matchers}

import scala.annotation.tailrec
import scala.collection.{LinearSeq, SeqView}
import scala.runtime.NonLocalReturnControl

/**
* @author 杨博 (Yang Bo)
*/
class YieldSpec213 extends FreeSpec with Matchers {

"lazylist" - {

def shouldCompile = {
!Yield("naked")
LazyList.empty[String]
}

"local method" in {
def generator: LazyList[Int] = {
def id[A](a: A) = a
id(!Yield(100))
LazyList.empty[Int]
}
generator should be(LazyList(100))
}

"yield from" in {
def generator: LazyList[Int] = {
def id[A](a: A) = a
id(!Yield(100, 200))
LazyList.empty
}
generator should be(LazyList(100, 200))
}

"local function" in {
def generator: LazyList[Int] = {
def id[A](a: A) = a
(id[Unit] _)(!Yield(100))
LazyList.empty[Int]
}
generator should be(LazyList(100))
}

"do/while" - {
"empty body" in {
def generator: LazyList[Int] = {
do {} while ({
!Yield(100)
false
})
LazyList.empty[Int]
}
generator should be(LazyList(100))
}

"false" in {
def generator: LazyList[Int] = {
do {
!Yield(100)
} while (false)
LazyList.empty[Int]
}
generator should be(LazyList(100))
}

"with var" in {
def generator: LazyList[Int] = {
var i = 5
do {
i -= {
!Yield(i)
1
}
} while ({
!Yield(-i)
i > 0
})
LazyList.empty[Int]
}
generator should be(LazyList(5, -4, 4, -3, 3, -2, 2, -1, 1, 0))
}
}

"while" - {
"false" in {
def whileFalse: LazyList[Int] = {
while (false) {
!Yield(100)
}
LazyList.empty[Int]
}

whileFalse should be(LazyList.empty)
}
}

"match/case" in {

def loop(i: Int): LazyList[Int] = {
i match {
case 100 =>
LazyList.empty
case _ =>
!Yield(i)
loop(i + 1)
}
}

loop(90) should be(LazyList(90, 91, 92, 93, 94, 95, 96, 97, 98, 99))

}

"recursive" in {
def loop(i: Int): LazyList[Int] = {
if (i < 100) {
!Yield(i)
loop(i + 1)
} else {
LazyList.empty
}
}

loop(90) should be(LazyList(90, 91, 92, 93, 94, 95, 96, 97, 98, 99))

}

"Given a generator that contains conditional Yield" - {
def generator = {
if (false) {
!Yield(0)
}
if (true) {
!Yield(1)
}
if ({ !Yield(2); false }) {
!Yield(3)
} else {
!Yield(4)
}
LazyList.empty[Int]
}

"Then the generator should contains values in selected branches" in {
generator should be(Seq(1, 2, 4))
}

}

"Given a continuation that uses Yield" - {

def yield4243: LazyList[Int] !! Unit = _ {
!Yield(42)
!Yield(43)
}

"when create a generator that contains multiple Yield expression followed by a bang notation and a LazyList.empty" - {

def generator: LazyList[Int] = {
!Yield(0)
!Shift(yield4243)
!Yield(1)
LazyList.empty[Int]
}

"Then the generator should contains yield values" in {
generator should be(Seq(0, 42, 43, 1))
}

}

}

"apply" in {
def generator: LazyList[Int] = {
val f = {
!Yield(1)

{ (x: Int) =>
-x
}
}

val result = f({
!Yield(2)
42
})
LazyList(result)
}
generator should be(LazyList(1, 2, -42))
}

"return" in {
def generator: LazyList[Int] = {
if (true) {
return {
!Yield(100)
LazyList(42)
}
}
LazyList.empty[Int]
}

a[NonLocalReturnControl[LazyList[Int]]] should be thrownBy generator.last
}
"partial function" - {
"empty" in {
Seq.empty[Any].flatMap {
case i: Int =>
!Yield(100)
LazyList(42)
} should be(empty)
}

"flatMap" in {
Seq(100, 200).flatMap {
case i: Int =>
!Yield(100)
LazyList(42 + i)
} should be(Seq(100, 142, 100, 242))
}
}

"nested function call" - {
"call by value" in {
def nested() = {
"foo" +: !Yield("bar") +: LazyList.empty[Any]
}
nested() should be(LazyList("bar", "foo", ()))
}
"call by name" in {
def nested() = {
"foo" #:: !Yield("bar") #:: LazyList.empty[Any]
}
nested() should be(LazyList("bar", "foo", ()))
}
}

}

}

0 comments on commit a55dd6f

Please sign in to comment.