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 all 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
4 changes: 1 addition & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@ language: scala

sudo: required

dist: trusty

group: edge

git:
depth: 9999

jdk:
- oraclejdk8
- openjdk8

scala_version_211: &scala_version_211 2.11.12
scala_version_212: &scala_version_212 2.12.9
Expand Down
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,12 @@
package cats

package data

abstract private[data] class ScalaVersionSpecificPackage
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)

def NonEmptyStream[A](head: A, tail: A*): NonEmptyStream[A] =
OneAnd(head, tail.toStream)
}
31 changes: 31 additions & 0 deletions core/src/main/scala-2.12-/cats/data/ZipStream.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package cats
package data

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

object ZipStream {

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(Stream.continually(x))

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

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

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(Stream.empty[A])

def combineK[A](x: ZipStream[A], y: ZipStream[A]): ZipStream[A] =
ZipStream(cats.instances.stream.catsStdInstancesForStream.combineK(x.value, y.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,108 @@
package cats
package instances

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

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
7 changes: 0 additions & 7 deletions core/src/main/scala-2.12-/cats/instances/stream.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,6 @@ import cats.syntax.show._

import scala.annotation.tailrec

/**
* For cross compile with backward compatibility
*/
trait LazyListInstances extends StreamInstances with StreamInstancesBinCompat0 {
val catsStdInstancesForLazyList = catsStdInstancesForStream
}

trait StreamInstances extends cats.kernel.instances.StreamInstances {

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

This file was deleted.

17 changes: 16 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 @@ -362,8 +362,23 @@ 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)}")

implicit def catsDataParallelForNonEmptyLazyList: Parallel[NonEmptyLazyList, OneAnd[ZipLazyList, *]] =
new Parallel[NonEmptyLazyList, OneAnd[ZipLazyList, *]] {

def applicative: Applicative[OneAnd[ZipLazyList, *]] =
OneAnd.catsDataApplicativeForOneAnd(ZipLazyList.catsDataAlternativeForZipLazyList)
LukaJCB marked this conversation as resolved.
Show resolved Hide resolved
def monad: Monad[NonEmptyLazyList] = NonEmptyLazyList.catsDataInstancesForNonEmptyLazyList

def sequential: OneAnd[ZipLazyList, *] ~> NonEmptyLazyList =
λ[OneAnd[ZipLazyList, *] ~> NonEmptyLazyList](
znell => NonEmptyLazyList.fromLazyListPrepend(znell.head, znell.tail.value)
)

def parallel: NonEmptyLazyList ~> OneAnd[ZipLazyList, *] =
λ[NonEmptyLazyList ~> OneAnd[ZipLazyList, *]](nell => OneAnd(nell.head, ZipLazyList(nell.tail)))
}
}

sealed abstract private[data] class NonEmptyLazyListInstances1 extends NonEmptyLazyListInstances2 {
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,16 @@
package cats

package data

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)

@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