Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
kailuowang committed Jun 20, 2019
1 parent d822620 commit 1db391d
Show file tree
Hide file tree
Showing 24 changed files with 125 additions and 50 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jdk:
- oraclejdk8

scala_version_211: &scala_version_211 2.11.12
scala_version_212: &scala_version_212 2.12.7
scala_version_212: &scala_version_212 2.12.8
scala_version_213: &scala_version_213 2.13.0

before_install:
Expand Down
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ crossScalaVersionsFromTravis in Global := {

def scalaVersionSpecificFolders(srcName: String, srcBaseDir: java.io.File, scalaVersion: String) = {
def extraDirs(suffix: String) =
CrossType.Pure.sharedSrcDir(srcBaseDir, "main").toList.map(f => file(f.getPath + suffix))
List(CrossType.Pure, CrossType.Full).flatMap(_.sharedSrcDir(srcBaseDir, srcName).toList.map(f => file(f.getPath + suffix)))
CrossVersion.partialVersion(scalaVersion) match {
case Some((2, y)) if y <= 12 =>
extraDirs("-2.12-")
Expand Down
10 changes: 10 additions & 0 deletions core/src/main/scala-2.12-/cats/compat/StreamOps.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package cats.compat

object StreamOps {
def toStream[A](traversableOnce: TraversableOnce[A]): Stream[A] = traversableOnce.toStream

def emptyStream[A]: Stream[A] = Stream.empty[A]

def streamString: String = "Stream"

}
9 changes: 9 additions & 0 deletions core/src/main/scala-2.13+/cats/compat/StreamOps.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package cats.compat

object StreamOps {
def toStream[A](io: IterableOnce[A]): LazyList[A] = LazyList.from(io)

def emptyStream[A]: LazyList[A] = LazyList.empty[A]

def streamString: String = "LazyList"
}
1 change: 1 addition & 0 deletions core/src/main/scala/cats/data/OneAnd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package data
import scala.annotation.tailrec
import scala.collection.mutable.Builder
import cats.instances.stream._
import kernel.compat.Stream

/**
* A data type which represents a single element (head) and some other
Expand Down
7 changes: 4 additions & 3 deletions core/src/main/scala/cats/data/ZipStream.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package cats.data
package cats
package data

import cats.{Alternative, CommutativeApplicative, Eq}
import cats.instances.stream._
import instances.stream._
import kernel.compat.Stream

class ZipStream[A](val value: Stream[A]) extends AnyVal

Expand Down
6 changes: 4 additions & 2 deletions core/src/main/scala/cats/data/package.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package cats

import kernel.compat.Stream
import compat.StreamOps.toStream
package object data {

type NonEmptyStream[A] = OneAnd[Stream, A]
type ValidatedNel[+E, +A] = Validated[NonEmptyList[E], A]
type IorNel[+B, +A] = Ior[NonEmptyList[B], A]
Expand All @@ -14,7 +16,7 @@ package object data {
def NonEmptyStream[A](head: A, tail: Stream[A] = Stream.empty): NonEmptyStream[A] =
OneAnd(head, tail)
def NonEmptyStream[A](head: A, tail: A*): NonEmptyStream[A] =
OneAnd(head, tail.toStream)
OneAnd(head, toStream(tail))

type NonEmptyMap[K, +A] = NonEmptyMapImpl.Type[K, A]
val NonEmptyMap = NonEmptyMapImpl
Expand Down
4 changes: 3 additions & 1 deletion core/src/main/scala/cats/instances/parallel.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package cats.instances
package cats
package instances

import cats.data._
import cats.kernel.Semigroup
import cats.syntax.either._
import cats.{~>, Applicative, Apply, FlatMap, Functor, Monad, NonEmptyParallel, Parallel}
import kernel.compat.Stream

trait ParallelInstances extends ParallelInstances1 {
implicit def catsParallelForEitherValidated[E: Semigroup]: Parallel[Either[E, ?], Validated[E, ?]] =
Expand Down
7 changes: 4 additions & 3 deletions core/src/main/scala/cats/instances/sortedSet.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import cats.kernel.{BoundedSemilattice, Hash, Order}
import scala.collection.immutable.SortedSet
import scala.annotation.tailrec
import cats.implicits._
import compat.StreamOps._

trait SortedSetInstances extends SortedSetInstances1 {

Expand Down Expand Up @@ -92,13 +93,13 @@ trait SortedSetInstancesBinCompat0 {
class SortedSetOrder[A: Order] extends Order[SortedSet[A]] {
def compare(a1: SortedSet[A], a2: SortedSet[A]): Int =
Order[Int].compare(a1.size, a2.size) match {
case 0 => Order.compare(a1.toStream, a2.toStream)
case 0 => Order.compare(toStream(a1), toStream(a2))
case x => x
}

override def eqv(s1: SortedSet[A], s2: SortedSet[A]): Boolean = {
implicit val x = Order[A].toOrdering
s1.toStream.corresponds(s2.toStream)(Order[A].eqv)
toStream(s1).corresponds(toStream(s2))(Order[A].eqv)
}
}

Expand All @@ -125,7 +126,7 @@ class SortedSetHash[A: Order: Hash] extends Hash[SortedSet[A]] {
}
override def eqv(s1: SortedSet[A], s2: SortedSet[A]): Boolean = {
implicit val x = Order[A].toOrdering
s1.toStream.corresponds(s2.toStream)(Order[A].eqv)
toStream(s1).corresponds(toStream(s2))(Order[A].eqv)
}
}

Expand Down
19 changes: 10 additions & 9 deletions core/src/main/scala/cats/instances/stream.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cats
package instances

import kernel.compat.Stream
import compat.StreamOps._
import cats.syntax.show._

import scala.annotation.tailrec
Expand All @@ -10,9 +11,9 @@ trait StreamInstances extends cats.kernel.instances.StreamInstances {
: Traverse[Stream] with Alternative[Stream] with Monad[Stream] with CoflatMap[Stream] =
new Traverse[Stream] with Alternative[Stream] with Monad[Stream] with CoflatMap[Stream] {

def empty[A]: Stream[A] = Stream.Empty
def empty[A]: Stream[A] = emptyStream

def combineK[A](x: Stream[A], y: Stream[A]): Stream[A] = x #::: y
def combineK[A](x: Stream[A], y: Stream[A]): Stream[A] = x lazyAppendedAll y

def pure[A](x: A): Stream[A] = Stream(x)

Expand All @@ -31,7 +32,7 @@ trait StreamInstances extends cats.kernel.instances.StreamInstances {
else fb.map(fb => map2(fa, fb)(f))

def coflatMap[A, B](fa: Stream[A])(f: Stream[A] => B): Stream[B] =
fa.tails.toStream.init.map(f)
toStream(fa.tails).init.map(f)

def foldLeft[A, B](fa: Stream[A], b: B)(f: (B, A) => B): B =
fa.foldLeft(b)(f)
Expand Down Expand Up @@ -71,7 +72,7 @@ trait StreamInstances extends cats.kernel.instances.StreamInstances {
stack = tail
state = Right(Some(b))
case Left(a) #:: tail =>
stack = fn(a) #::: tail
stack = (fn(a) #::: tail).force
advance()
case empty =>
state = Right(None)
Expand All @@ -98,7 +99,7 @@ trait StreamInstances extends cats.kernel.instances.StreamInstances {
}
}

it.toStream
toStream(it)
}

override def exists[A](fa: Stream[A])(p: A => Boolean): Boolean =
Expand Down Expand Up @@ -153,7 +154,7 @@ trait StreamInstances extends cats.kernel.instances.StreamInstances {

implicit def catsStdShowForStream[A: Show]: Show[Stream[A]] =
new Show[Stream[A]] {
def show(fa: Stream[A]): String = if (fa.isEmpty) "Stream()" else s"Stream(${fa.head.show}, ?)"
def show(fa: Stream[A]): String = if (fa.isEmpty) s"$streamString()" else s"$streamString(${fa.head.show}, ?)"
}
}

Expand All @@ -171,13 +172,13 @@ trait StreamInstancesBinCompat0 {
override def flattenOption[A](fa: Stream[Option[A]]): Stream[A] = fa.flatten

def traverseFilter[G[_], A, B](fa: Stream[A])(f: (A) => G[Option[B]])(implicit G: Applicative[G]): G[Stream[B]] =
fa.foldRight(Eval.now(G.pure(Stream.empty[B])))(
fa.foldRight(Eval.now(G.pure(emptyStream[B])))(
(x, xse) => G.map2Eval(f(x), xse)((i, o) => i.fold(o)(_ +: o))
)
.value

override def filterA[G[_], A](fa: Stream[A])(f: (A) => G[Boolean])(implicit G: Applicative[G]): G[Stream[A]] =
fa.foldRight(Eval.now(G.pure(Stream.empty[A])))(
fa.foldRight(Eval.now(G.pure(emptyStream[A])))(
(x, xse) => G.map2Eval(f(x), xse)((b, as) => if (b) x +: as else as)
)
.value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import org.scalatest.funsuite.AnyFunSuiteLike
import scala.concurrent.duration.{Duration, FiniteDuration}
import scala.collection.immutable.{BitSet, Queue}
import scala.util.Random

import compat.Stream
import java.util.UUID
import java.util.concurrent.TimeUnit.{DAYS, HOURS, MICROSECONDS, MILLISECONDS, MINUTES, NANOSECONDS, SECONDS}

Expand Down
5 changes: 5 additions & 0 deletions kernel/src/main/scala-2.12-/cats/kernel/compat/package.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package cats.kernel

package object compat {
type Stream[+A] = scala.Stream[A]
}
8 changes: 8 additions & 0 deletions kernel/src/main/scala-2.13+/cats/kernel/compat/package.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package cats
package kernel

package object compat {
type Stream[+A] = scala.LazyList[A]

val Stream = LazyList
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package cats.kernel
package instances
import compat.Stream

trait StreamInstances extends StreamInstances1 {
implicit def catsKernelStdOrderForStream[A: Order]: Order[Stream[A]] =
Expand Down
2 changes: 1 addition & 1 deletion laws/src/main/scala/cats/laws/discipline/Arbitrary.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package cats
package laws
package discipline

import kernel.compat.Stream
import cats.data.NonEmptyList.ZipNonEmptyList
import cats.data.NonEmptyVector.ZipNonEmptyVector
import scala.util.{Failure, Success, Try}
Expand Down
14 changes: 14 additions & 0 deletions tests/src/test/scala-2.12-/cats/tests/StreamMonadSuite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package cats
package tests

import laws.discipline.MonadTests
import data.NonEmptyStream
import cats.laws.discipline.arbitrary._


class StreamMonadSuite extends CatsSuite {
checkAll("Stream[Int]", MonadTests[Stream].monad[Int, Int, Int])
checkAll("NonEmptyStream[Int]", MonadTests[NonEmptyStream].monad[Int, Int, Int])


}
13 changes: 13 additions & 0 deletions tests/src/test/scala-2.13+/cats/tests/LazyListMonadSuite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package cats
package tests

import cats.laws.discipline.MonadTests
import cats.data.NonEmptyStream
import cats.laws.discipline.arbitrary._

class LazyListMonadSuite extends CatsSuite {

//todo: fix Monad[LazyList] implementation to make it stack safe
checkAll("LazyList[Int]", MonadTests[LazyList].monad[Int, Int, Int])
checkAll("NonEmptyStream[Int]", MonadTests[NonEmptyStream].stackUnsafeMonad[Int, Int, Int])
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import cats.implicits._
import cats.Eq
import cats.kernel.laws.discipline.{MonoidTests, SemigroupTests}
import org.scalacheck.{Arbitrary, Gen}
import kernel.compat.Stream


object BinCodecInvariantMonoidalSuite {
final case class MiniList[+A] private (val toList: List[A]) extends AnyVal {
Expand Down
26 changes: 16 additions & 10 deletions tests/src/test/scala/cats/tests/FoldableSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import scala.collection.immutable._
import cats.instances.all._
import cats.data._
import cats.laws.discipline.arbitrary._
import kernel.compat.Stream
import compat.StreamOps.toStream

abstract class FoldableSuite[F[_]: Foldable](name: String)(implicit ArbFInt: Arbitrary[F[Int]],
ArbFString: Arbitrary[F[String]])
Expand Down Expand Up @@ -336,7 +338,7 @@ class FoldableSuiteAdditional extends CatsSuite {
}

test("Foldable[Stream].foldM stack safety") {
checkMonadicFoldsStackSafety[Stream](_.toStream)
checkMonadicFoldsStackSafety[Stream](toStream)
}

test("Foldable[Vector].foldM/existsM/forallM/findM/collectFirstSomeM stack safety") {
Expand Down Expand Up @@ -365,32 +367,36 @@ class FoldableSuiteAdditional extends CatsSuite {
checkMonadicFoldsStackSafety[NonEmptyStream](xs => NonEmptyStream(xs.head, xs.tail: _*))
}

test("Foldable[Stream]") {
val F = Foldable[Stream]
val F = Foldable[Stream]
def bomb[A]: A = sys.error("boom")
val dangerous = 0 #:: 1 #:: 2 #:: bomb[Stream[Int]]

def bomb[A]: A = sys.error("boom")
val dangerous = 0 #:: 1 #:: 2 #:: bomb[Stream[Int]]
test("Foldable[Stream] doesn't blow up") {

// doesn't blow up - this also ensures it works for infinite streams.
assert(contains(dangerous, 2).value)
}

// lazy results don't blow up unless you call .value on them.
test("lazy results don't blow up unless you call .value on them") {
val doom: Eval[Boolean] = contains(dangerous, -1)
}

// ensure that the Lazy[B] param to foldRight is actually being
// handled lazily. it only needs to be evaluated if we reach the
test("Lazy[B] param to foldRight is actually being handled lazily") {
// ensure that the . it only needs to be evaluated if we reach the
// "end" of the fold.
val trap = Eval.later(bomb[Boolean])
val result = F.foldRight(1 #:: 2 #:: Stream.empty, trap) { (n, lb) =>
if (n == 2) Now(true) else lb
}
assert(result.value)
}

// test trampolining
test("trampolining") {
val large = Stream((1 to 10000): _*)
assert(contains(large, 10000).value)
}

// test laziness of foldM
test("laziness of foldM"){
dangerous.foldM(0)((acc, a) => if (a < 2) Some(acc + a) else None) should ===(None)

}
Expand Down
9 changes: 5 additions & 4 deletions tests/src/test/scala/cats/tests/OneAndSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import cats.laws.discipline.{
TraverseTests
}
import cats.laws.discipline.arbitrary._

import kernel.compat.Stream
import compat.StreamOps.toStream
class OneAndSuite extends CatsSuite {
// Lots of collections here.. telling ScalaCheck to calm down a bit
implicit override val generatorDrivenConfig: PropertyCheckConfiguration =
Expand Down Expand Up @@ -87,7 +88,7 @@ class OneAndSuite extends CatsSuite {

implicit val iso2 = SemigroupalTests.Isomorphisms.invariant[OneAnd[Stream, ?]]

checkAll("NonEmptyStream[Int]", MonadTests[NonEmptyStream].monad[Int, Int, Int])

checkAll("Monad[NonEmptyStream[A]]", SerializableTests.serializable(Monad[NonEmptyStream]))

checkAll("NonEmptyStream[Int]", ComonadTests[NonEmptyStream].comonad[Int, Int, Int])
Expand All @@ -110,7 +111,7 @@ class OneAndSuite extends CatsSuite {

test("Show is formatted correctly") {
val oneAnd = NonEmptyStream("Test")
oneAnd.show should ===("OneAnd(Test, Stream())")
oneAnd.show should ===(s"OneAnd(Test, ${compat.StreamOps.streamString}())")
}

test("Creating OneAnd + unwrap is identity") {
Expand Down Expand Up @@ -224,6 +225,6 @@ class ReducibleNonEmptyStreamSuite extends ReducibleSuite[NonEmptyStream]("NonEm
// if we inline this we get a bewildering implicit numeric widening
// error message in Scala 2.10
val tailStart: Long = start + 1L
NonEmptyStream(start, (tailStart).to(endInclusive).toStream)
NonEmptyStream(start, toStream(tailStart.to(endInclusive)))
}
}
1 change: 1 addition & 0 deletions tests/src/test/scala/cats/tests/ParallelSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import cats.laws.discipline.eq._
import cats.laws.discipline.arbitrary._
import org.typelevel.discipline.scalatest.Discipline
import scala.collection.immutable.SortedSet
import kernel.compat.Stream

class ParallelSuite extends CatsSuite with ApplicativeErrorForEitherTest {

Expand Down
2 changes: 2 additions & 0 deletions tests/src/test/scala/cats/tests/RegressionSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ package tests
import cats.data.{Const, NonEmptyList, StateT}
import scala.collection.mutable
import scala.collection.immutable.SortedMap
import kernel.compat.Stream

class RegressionSuite extends CatsSuite {

// toy state class
Expand Down
Loading

0 comments on commit 1db391d

Please sign in to comment.