Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Treat Stream and LazyList as different types #2983

Merged
merged 20 commits into from
Aug 22, 2019
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
4edcd9e
Remove cats.instances.stream and introduce cats.kernel.instances.lazy…
travisbrown Aug 9, 2019
73c389f
Merge branch 'master' into topic/stream-instances
travisbrown Aug 15, 2019
dd79248
Merge branch 'master' into topic/stream-instances
travisbrown Aug 19, 2019
d843f04
Reinstate stream instances on 2.13
travisbrown Aug 19, 2019
6f8658b
Don't alias LazyList to Stream pre-2.13
travisbrown Aug 19, 2019
67dc634
Fix duplicate test names
travisbrown Aug 19, 2019
40333b2
Don't use scalaVersionSpecific helpers in version-specific code
travisbrown Aug 20, 2019
c316ca2
Add LazyList tests for kernel
travisbrown Aug 20, 2019
7806c4c
Parallel laws checking for NonEmptyLazyList
travisbrown Aug 20, 2019
e8dcc68
Provide Parallel instance for NonEmptyLazyList via OneAnd and ZipLazy…
travisbrown Aug 20, 2019
66742d7
Avoid LazyListInstances in non-2.13 code
travisbrown Aug 20, 2019
f9ab0e2
Merge branch 'master' into topic/stream-instances
travisbrown Aug 21, 2019
bf10981
Desperate attempt to fix Travis CI weirdness
travisbrown Aug 21, 2019
ae7b6e5
Revert "Desperate attempt to fix Travis CI weirdness"
travisbrown Aug 21, 2019
9b28588
Another desperate attempt
travisbrown Aug 21, 2019
f80d9ab
Revert "Another desperate attempt"
travisbrown Aug 21, 2019
fd972c5
Switch to OpenJDK 8
travisbrown Aug 21, 2019
7e52796
Reinstate Stream tests
travisbrown Aug 22, 2019
a2cdc6b
...with the right types this time
travisbrown Aug 22, 2019
6a1c334
Add regression tests for #513 for LazyList on 2.13
travisbrown Aug 22, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,7 @@ lazy val kernelLaws = crossProject(JSPlatform, JVMPlatform)
.settings(scoverageSettings)
.settings(disciplineDependencies)
.settings(testingDependencies)
.settings(scalacOptions in Test := (scalacOptions in Test).value.filter(_ != "-Xfatal-warnings"))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is necessary because we now have deprecation warnings for Stream on 2.13.

.jsSettings(commonJsSettings)
.jvmSettings(commonJvmSettings)
.jsSettings(coverageEnabled := false)
Expand Down
9 changes: 0 additions & 9 deletions core/src/main/scala-2.12-/cats/compat/lazyList.scala

This file was deleted.

26 changes: 26 additions & 0 deletions core/src/main/scala-2.12-/cats/data/OneAndLowPriority4.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package cats
package data

import scala.annotation.tailrec
import scala.collection.mutable.Builder

abstract private[data] class OneAndLowPriority4 {
implicit val catsDataComonadForNonEmptyStream: Comonad[OneAnd[Stream, *]] =
new Comonad[OneAnd[Stream, *]] {
def coflatMap[A, B](fa: OneAnd[Stream, A])(f: OneAnd[Stream, A] => B): OneAnd[Stream, B] = {
@tailrec def consume(as: Stream[A], buf: Builder[B, Stream[B]]): Stream[B] =
if (as.isEmpty) buf.result
else {
val tail = as.tail
consume(tail, buf += f(OneAnd(as.head, tail)))
}
OneAnd(f(fa), consume(fa.tail, Stream.newBuilder))
}

def extract[A](fa: OneAnd[Stream, A]): A =
fa.head

def map[A, B](fa: OneAnd[Stream, A])(f: A => B): OneAnd[Stream, B] =
fa.map(f)(cats.instances.stream.catsStdInstancesForStream)
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
package cats

package data

abstract private[data] class ScalaVersionSpecificPackage
import kernel.compat.scalaVersionSpecific._

abstract private[data] class ScalaVersionSpecificPackage {
type NonEmptyStream[A] = OneAnd[Stream, A]

def NonEmptyStream[A](head: A, tail: Stream[A] = Stream.empty): NonEmptyStream[A] =
OneAnd(head, tail)

@suppressUnusedImportWarningForScalaVersionSpecific
def NonEmptyStream[A](head: A, tail: A*): NonEmptyStream[A] =
OneAnd(head, tail.toStream)
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
package cats
package data

import instances.stream._
import kernel.compat.scalaVersionSpecific._

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

@suppressUnusedImportWarningForScalaVersionSpecific
object ZipStream {

def apply[A](value: LazyList[A]): ZipStream[A] = new ZipStream(value)
def apply[A](value: Stream[A]): ZipStream[A] = new ZipStream(value)

implicit val catsDataAlternativeForZipStream: Alternative[ZipStream] with CommutativeApplicative[ZipStream] =
new Alternative[ZipStream] with CommutativeApplicative[ZipStream] {
def pure[A](x: A): ZipStream[A] = new ZipStream(LazyList.continually(x))
def pure[A](x: A): ZipStream[A] = new ZipStream(Stream.continually(x))

override def map[A, B](fa: ZipStream[A])(f: (A) => B): ZipStream[B] =
ZipStream(fa.value.map(f))
Expand All @@ -24,11 +23,12 @@ object ZipStream {
override def product[A, B](fa: ZipStream[A], fb: ZipStream[B]): ZipStream[(A, B)] =
ZipStream(fa.value.zip(fb.value))

def empty[A]: ZipStream[A] = ZipStream(LazyList.empty[A])
def empty[A]: ZipStream[A] = ZipStream(Stream.empty[A])

def combineK[A](x: ZipStream[A], y: ZipStream[A]): ZipStream[A] =
ZipStream(Alternative[LazyList].combineK(x.value, y.value))
ZipStream(cats.instances.stream.catsStdInstancesForStream.combineK(x.value, y.value))
}

implicit def catsDataEqForZipStream[A: Eq]: Eq[ZipStream[A]] = Eq.by(_.value)
implicit def catsDataEqForZipStream[A: Eq]: Eq[ZipStream[A]] =
Eq.by((_: ZipStream[A]).value)(cats.kernel.instances.stream.catsKernelStdEqForStream[A])
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
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.scalaVersionSpecific._

@suppressUnusedImportWarningForScalaVersionSpecific
trait ParallelInstances extends ParallelInstances1 {
implicit def catsParallelForEitherValidated[E: Semigroup]: Parallel[Either[E, *], Validated[E, *]] =
new Parallel[Either[E, *], Validated[E, *]] {

def applicative: Applicative[Validated[E, *]] = Validated.catsDataApplicativeErrorForValidated
def monad: Monad[Either[E, *]] = cats.instances.either.catsStdInstancesForEither

def sequential: Validated[E, *] ~> Either[E, *] =
λ[Validated[E, *] ~> Either[E, *]](_.toEither)

def parallel: Either[E, *] ~> Validated[E, *] =
λ[Either[E, *] ~> Validated[E, *]](_.toValidated)
}

implicit def catsParallelForOptionTNestedOption[F[_], M[_]](
implicit P: Parallel[M, F]
): Parallel[OptionT[M, *], Nested[F, Option, *]] = new Parallel[OptionT[M, *], Nested[F, Option, *]] {

implicit val appF: Applicative[F] = P.applicative
implicit val monadM: Monad[M] = P.monad
implicit val appOption: Applicative[Option] = cats.instances.option.catsStdInstancesForOption

def applicative: Applicative[Nested[F, Option, *]] = cats.data.Nested.catsDataApplicativeForNested[F, Option]

def monad: Monad[OptionT[M, *]] = cats.data.OptionT.catsDataMonadErrorMonadForOptionT[M]

def sequential: Nested[F, Option, *] ~> OptionT[M, *] =
λ[Nested[F, Option, *] ~> OptionT[M, *]](nested => OptionT(P.sequential(nested.value)))

def parallel: OptionT[M, *] ~> Nested[F, Option, *] =
λ[OptionT[M, *] ~> Nested[F, Option, *]](optT => Nested(P.parallel(optT.value)))
}

implicit def catsStdNonEmptyParallelForZipList[A]: NonEmptyParallel[List, ZipList] =
new NonEmptyParallel[List, ZipList] {

def flatMap: FlatMap[List] = cats.instances.list.catsStdInstancesForList
def apply: Apply[ZipList] = ZipList.catsDataCommutativeApplyForZipList

def sequential: ZipList ~> List =
λ[ZipList ~> List](_.value)

def parallel: List ~> ZipList =
λ[List ~> ZipList](v => new ZipList(v))
}

implicit def catsStdNonEmptyParallelForZipVector[A]: NonEmptyParallel[Vector, ZipVector] =
new NonEmptyParallel[Vector, ZipVector] {

def flatMap: FlatMap[Vector] = cats.instances.vector.catsStdInstancesForVector
def apply: Apply[ZipVector] = ZipVector.catsDataCommutativeApplyForZipVector

def sequential: ZipVector ~> Vector =
λ[ZipVector ~> Vector](_.value)

def parallel: Vector ~> ZipVector =
λ[Vector ~> ZipVector](v => new ZipVector(v))
}

implicit def catsStdParallelForZipStream[A]: Parallel[Stream, ZipStream] =
new Parallel[Stream, ZipStream] {

def monad: Monad[Stream] = cats.instances.stream.catsStdInstancesForStream
def applicative: Applicative[ZipStream] = ZipStream.catsDataAlternativeForZipStream

def sequential: ZipStream ~> Stream =
λ[ZipStream ~> Stream](_.value)

def parallel: Stream ~> ZipStream =
λ[Stream ~> ZipStream](v => new ZipStream(v))
}

implicit def catsParallelForEitherTNestedParallelValidated[F[_], M[_], E: Semigroup](
implicit P: Parallel[M, F]
): Parallel[EitherT[M, E, *], Nested[F, Validated[E, *], *]] =
new Parallel[EitherT[M, E, *], Nested[F, Validated[E, *], *]] {

implicit val appF: Applicative[F] = P.applicative
implicit val monadM: Monad[M] = P.monad
implicit val appValidated: Applicative[Validated[E, *]] = Validated.catsDataApplicativeErrorForValidated
implicit val monadEither: Monad[Either[E, *]] = cats.instances.either.catsStdInstancesForEither

def applicative: Applicative[Nested[F, Validated[E, *], *]] =
cats.data.Nested.catsDataApplicativeForNested[F, Validated[E, *]]

def monad: Monad[EitherT[M, E, *]] = cats.data.EitherT.catsDataMonadErrorForEitherT

def sequential: Nested[F, Validated[E, *], *] ~> EitherT[M, E, *] =
λ[Nested[F, Validated[E, *], *] ~> EitherT[M, E, *]] { nested =>
val mva = P.sequential(nested.value)
EitherT(Functor[M].map(mva)(_.toEither))
}

def parallel: EitherT[M, E, *] ~> Nested[F, Validated[E, *], *] =
λ[EitherT[M, E, *] ~> Nested[F, Validated[E, *], *]] { eitherT =>
val fea = P.parallel(eitherT.value)
Nested(Functor[F].map(fea)(_.toValidated))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ package object instances {
object sortedMap extends SortedMapInstances with SortedMapInstancesBinCompat0 with SortedMapInstancesBinCompat1
object sortedSet extends SortedSetInstances with SortedSetInstancesBinCompat0
object stream extends StreamInstances with StreamInstancesBinCompat0
object lazyList extends LazyListInstances
object string extends StringInstances
object try_ extends TryInstances
object tuple extends TupleInstances with Tuple2InstancesBinCompat0
Expand Down
6 changes: 2 additions & 4 deletions core/src/main/scala-2.12-/cats/instances/stream.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@ import cats.syntax.show._
import scala.annotation.tailrec

/**
* For cross compile with backward compatibility
* Needed only to avoid some version-specific code in `cats.instances.all`.
*/
trait LazyListInstances extends StreamInstances with StreamInstancesBinCompat0 {
val catsStdInstancesForLazyList = catsStdInstancesForStream
}
private[instances] trait LazyListInstances
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this still needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we don't want two copies of cats/instances/all.scala we do need it.

(I guess you could argue that we have two copies of everything else at this point so we might as well do it here.)

Copy link
Contributor

@kailuowang kailuowang Aug 20, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

didn't you already add version specific copies for cats/instances/package.scala?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but the definition of AllInstances in all.scala also needs to bring in the LazyList instances on 2.13.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah i see. I didn't see you were able to deprecate all the methods in StreamInstance to avoid deprecating the trait.
Since it confused me a bit, I think we might just as well create two copies for it so that hopefully we can get rid off all Stream/LazyList related ScalaVersionSpecific stuff in non test code?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I can go ahead and do this.


trait StreamInstances extends cats.kernel.instances.StreamInstances {

Expand Down
7 changes: 0 additions & 7 deletions core/src/main/scala-2.13+/cats/compat/lazyList.scala

This file was deleted.

40 changes: 39 additions & 1 deletion core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,29 @@ object NonEmptyLazyList extends NonEmptyLazyListInstances {

implicit def catsNonEmptyLazyListOps[A](value: NonEmptyLazyList[A]): NonEmptyLazyListOps[A] =
new NonEmptyLazyListOps(value)

class ZipNonEmptyLazyList[A](val value: NonEmptyLazyList[A]) extends Serializable

object ZipNonEmptyLazyList {

def apply[A](nell: NonEmptyLazyList[A]): ZipNonEmptyLazyList[A] =
new ZipNonEmptyLazyList(nell)

implicit val catsDataCommutativeApplyForZipNonEmptyLazyList: CommutativeApply[ZipNonEmptyLazyList] =
new CommutativeApply[ZipNonEmptyLazyList] {
def ap[A, B](ff: ZipNonEmptyLazyList[A => B])(fa: ZipNonEmptyLazyList[A]): ZipNonEmptyLazyList[B] =
ZipNonEmptyLazyList(ff.value.zipWith(fa.value)(_.apply(_)))

override def map[A, B](fa: ZipNonEmptyLazyList[A])(f: (A) => B): ZipNonEmptyLazyList[B] =
ZipNonEmptyLazyList(fa.value.map(f))

override def product[A, B](fa: ZipNonEmptyLazyList[A],
fb: ZipNonEmptyLazyList[B]): ZipNonEmptyLazyList[(A, B)] =
ZipNonEmptyLazyList(fa.value.zipWith(fb.value) { case (a, b) => (a, b) })
}

implicit def catsDataEqForZipNonEmptyLazyList[A: Eq]: Eq[ZipNonEmptyLazyList[A]] = Eq.by(_.value)
}
}

class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A]) extends AnyVal {
Expand Down Expand Up @@ -362,7 +385,22 @@ sealed abstract private[data] class NonEmptyLazyListInstances extends NonEmptyLa
Semigroup[LazyList[A]].asInstanceOf[Semigroup[NonEmptyLazyList[A]]]

implicit def catsDataShowForNonEmptyLazyList[A](implicit A: Show[A]): Show[NonEmptyLazyList[A]] =
Show.show[NonEmptyLazyList[A]](nec => s"NonEmpty${Show[LazyList[A]].show(nec.toLazyList)}")
Show.show[NonEmptyLazyList[A]](nell => s"NonEmpty${Show[LazyList[A]].show(nell.toLazyList)}")

import NonEmptyLazyList.ZipNonEmptyLazyList

implicit def catsDataParallelForNonEmptyLazyList: NonEmptyParallel[NonEmptyLazyList, ZipNonEmptyLazyList] =
new NonEmptyParallel[NonEmptyLazyList, ZipNonEmptyLazyList] {

def apply: Apply[ZipNonEmptyLazyList] = ZipNonEmptyLazyList.catsDataCommutativeApplyForZipNonEmptyLazyList
def flatMap: FlatMap[NonEmptyLazyList] = NonEmptyLazyList.catsDataInstancesForNonEmptyLazyList

def sequential: ZipNonEmptyLazyList ~> NonEmptyLazyList =
λ[ZipNonEmptyLazyList ~> NonEmptyLazyList](_.value)

def parallel: NonEmptyLazyList ~> ZipNonEmptyLazyList =
λ[NonEmptyLazyList ~> ZipNonEmptyLazyList](nell => new ZipNonEmptyLazyList(nell))
}

}

Expand Down
46 changes: 46 additions & 0 deletions core/src/main/scala-2.13+/cats/data/OneAndLowPriority4.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package cats
package data

import scala.annotation.tailrec
import scala.collection.mutable.Builder

abstract private[data] class OneAndLowPriority4 {
@deprecated("2.0.0-RC2", "Use catsDataComonadForNonEmptyLazyList")
implicit def catsDataComonadForNonEmptyStream: Comonad[OneAnd[Stream, *]] =
new Comonad[OneAnd[Stream, *]] {
def coflatMap[A, B](fa: OneAnd[Stream, A])(f: OneAnd[Stream, A] => B): OneAnd[Stream, B] = {
@tailrec def consume(as: Stream[A], buf: Builder[B, Stream[B]]): Stream[B] =
if (as.isEmpty) buf.result
else {
val tail = as.tail
consume(tail, buf += f(OneAnd(as.head, tail)))
}
OneAnd(f(fa), consume(fa.tail, Stream.newBuilder))
}

def extract[A](fa: OneAnd[Stream, A]): A =
fa.head

def map[A, B](fa: OneAnd[Stream, A])(f: A => B): OneAnd[Stream, B] =
fa.map(f)(cats.instances.stream.catsStdInstancesForStream)
}

implicit val catsDataComonadForNonEmptyLazyList: Comonad[OneAnd[LazyList, *]] =
new Comonad[OneAnd[LazyList, *]] {
def coflatMap[A, B](fa: OneAnd[LazyList, A])(f: OneAnd[LazyList, A] => B): OneAnd[LazyList, B] = {
@tailrec def consume(as: LazyList[A], buf: Builder[B, LazyList[B]]): LazyList[B] =
if (as.isEmpty) buf.result
else {
val tail = as.tail
consume(tail, buf += f(OneAnd(as.head, tail)))
}
OneAnd(f(fa), consume(fa.tail, LazyList.newBuilder))
}

def extract[A](fa: OneAnd[LazyList, A]): A =
fa.head

def map[A, B](fa: OneAnd[LazyList, A])(f: A => B): OneAnd[LazyList, B] =
fa.map(f)(cats.instances.lazyList.catsStdInstancesForLazyList)
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
package cats

package data

import kernel.compat.scalaVersionSpecific._

abstract private[data] class ScalaVersionSpecificPackage {
type NonEmptyLazyList[+A] = NonEmptyLazyList.Type[A]
@deprecated("2.0.0-RC2", "Use NonEmptyLazyList")
type NonEmptyStream[A] = OneAnd[Stream, A]

@deprecated("2.0.0-RC2", "Use NonEmptyLazyList")
def NonEmptyStream[A](head: A, tail: Stream[A]): NonEmptyStream[A] =
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that we can't have the default argument here that we have on 2.11 and 2.12, since it results in a deprecation warning (which is clearly a compiler bug).

OneAnd(head, tail)

@suppressUnusedImportWarningForScalaVersionSpecific
@deprecated("2.0.0-RC2", "Use NonEmptyLazyList")
def NonEmptyStream[A](head: A, tail: A*): NonEmptyStream[A] =
OneAnd(head, tail.toStream)
}
31 changes: 31 additions & 0 deletions core/src/main/scala-2.13+/cats/data/ZipLazyList.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package cats
package data

class ZipLazyList[A](val value: LazyList[A]) extends AnyVal

object ZipLazyList {

def apply[A](value: LazyList[A]): ZipLazyList[A] = new ZipLazyList(value)

implicit val catsDataAlternativeForZipLazyList: Alternative[ZipLazyList] with CommutativeApplicative[ZipLazyList] =
new Alternative[ZipLazyList] with CommutativeApplicative[ZipLazyList] {
def pure[A](x: A): ZipLazyList[A] = new ZipLazyList(LazyList.continually(x))

override def map[A, B](fa: ZipLazyList[A])(f: (A) => B): ZipLazyList[B] =
ZipLazyList(fa.value.map(f))

def ap[A, B](ff: ZipLazyList[A => B])(fa: ZipLazyList[A]): ZipLazyList[B] =
ZipLazyList(ff.value.lazyZip(fa.value).map(_.apply(_)))

override def product[A, B](fa: ZipLazyList[A], fb: ZipLazyList[B]): ZipLazyList[(A, B)] =
ZipLazyList(fa.value.zip(fb.value))

def empty[A]: ZipLazyList[A] = ZipLazyList(LazyList.empty[A])

def combineK[A](x: ZipLazyList[A], y: ZipLazyList[A]): ZipLazyList[A] =
ZipLazyList(cats.instances.lazyList.catsStdInstancesForLazyList.combineK(x.value, y.value))
}

implicit def catsDataEqForZipLazyList[A: Eq]: Eq[ZipLazyList[A]] =
Eq.by((_: ZipLazyList[A]).value)(cats.kernel.instances.lazyList.catsKernelStdEqForLazyList[A])
}
Loading