From b06073b9560dc77d965d4dd69eb846b7c2a13df4 Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Thu, 20 Jun 2019 21:40:42 -0400 Subject: [PATCH 01/12] make NonEmptyStreamSuite 2.12- specific --- .../cats/tests/NonEmptyStreamSuite.scala | 173 +++++++++++++++++ .../test/scala/cats/tests/OneAndSuite.scala | 174 +----------------- 2 files changed, 176 insertions(+), 171 deletions(-) create mode 100644 tests/src/test/scala-2.12-/cats/tests/NonEmptyStreamSuite.scala diff --git a/tests/src/test/scala-2.12-/cats/tests/NonEmptyStreamSuite.scala b/tests/src/test/scala-2.12-/cats/tests/NonEmptyStreamSuite.scala new file mode 100644 index 0000000000..2be2d3e85e --- /dev/null +++ b/tests/src/test/scala-2.12-/cats/tests/NonEmptyStreamSuite.scala @@ -0,0 +1,173 @@ +package cats +package tests + +import cats.data.{NonEmptyStream, OneAnd} +import cats.instances.stream._ +import cats.kernel.laws.discipline.{EqTests, SemigroupTests} +import cats.laws.discipline.arbitrary._ +import cats.laws.discipline._ + +class NonEmptyStreamSuite extends CatsSuite { + // Lots of collections here.. telling ScalaCheck to calm down a bit + implicit override val generatorDrivenConfig: PropertyCheckConfiguration = + PropertyCheckConfiguration(minSuccessful = 20, sizeRange = 5) + + checkAll("NonEmptyStream[Int]", EqTests[NonEmptyStream[Int]].eqv) + + checkAll("NonEmptyStream[Int] with Option", + NonEmptyTraverseTests[NonEmptyStream].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option]) + checkAll("NonEmptyTraverse[NonEmptyStream[A]]", SerializableTests.serializable(NonEmptyTraverse[NonEmptyStream[?]])) + + + checkAll("NonEmptyStream[Int]", ReducibleTests[NonEmptyStream].reducible[Option, Int, Int]) + checkAll("Reducible[NonEmptyStream]", SerializableTests.serializable(Reducible[NonEmptyStream])) + + + + checkAll("NonEmptyStream[Int]", SemigroupTests[NonEmptyStream[Int]].semigroup) + checkAll("Semigroup[NonEmptyStream[Int]]", SerializableTests.serializable(Semigroup[NonEmptyStream[Int]])) + + + { + // Test functor and subclasses don't have implicit conflicts + implicitly[Functor[NonEmptyStream]] + implicitly[Monad[NonEmptyStream]] + implicitly[Comonad[NonEmptyStream]] + } + + implicit val iso2 = SemigroupalTests.Isomorphisms.invariant[NonEmptyStream] + + 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]) + checkAll("Comonad[NonEmptyStream[A]]", SerializableTests.serializable(Comonad[NonEmptyStream])) + + test("Show is not empty and is formatted as expected") { + forAll { (nel: NonEmptyStream[Int]) => + nel.show.nonEmpty should ===(true) + nel.show.startsWith("OneAnd(") should ===(true) + nel.show should ===(implicitly[Show[NonEmptyStream[Int]]].show(nel)) + nel.show.contains(nel.head.show) should ===(true) + } + } + + test("Show is formatted correctly") { + val oneAnd = NonEmptyStream("Test") + oneAnd.show should ===(s"OneAnd(Test, Stream())") + } + + test("Creating OneAnd + unwrap is identity") { + forAll { (i: Int, tail: Stream[Int]) => + val stream = i #:: tail + val oneAnd = NonEmptyStream(i, tail: _*) + stream should ===(oneAnd.unwrap) + } + } + + test("NonEmptyStream#find is consistent with Stream#find") { + forAll { (nel: NonEmptyStream[Int], p: Int => Boolean) => + val stream = nel.unwrap + nel.find(p) should ===(stream.find(p)) + } + } + + test("NonEmptyStream#exists is consistent with Stream#exists") { + forAll { (nel: NonEmptyStream[Int], p: Int => Boolean) => + val stream = nel.unwrap + nel.exists(p) should ===(stream.exists(p)) + } + } + + test("NonEmptyStream#forall is consistent with Stream#forall") { + forAll { (nel: NonEmptyStream[Int], p: Int => Boolean) => + val stream = nel.unwrap + nel.forall(p) should ===(stream.forall(p)) + } + } + + test("NonEmptyStream#map is consistent with Stream#map") { + forAll { (nel: NonEmptyStream[Int], p: Int => String) => + val stream = nel.unwrap + nel.map(p).unwrap should ===(stream.map(p)) + } + } + + test("NonEmptyStream#nonEmptyPartition remains sorted") { + forAll { (nes: NonEmptyStream[Int], f: Int => Either[String, String]) => + val nesf = nes.map(f) + val sortedStream = (nesf.head #:: nesf.tail).sorted + val sortedNes = OneAnd(sortedStream.head, sortedStream.tail) + val ior = Reducible[NonEmptyStream].nonEmptyPartition(sortedNes)(identity) + + ior.left.map(xs => xs.sorted should ===(xs)) + ior.right.map(xs => xs.sorted should ===(xs)) + } + } + + test("reduceLeft consistent with foldLeft") { + forAll { (nel: NonEmptyStream[Int], f: (Int, Int) => Int) => + nel.reduceLeft(f) should ===(nel.tail.foldLeft(nel.head)(f)) + } + } + + test("reduceRight consistent with foldRight") { + forAll { (nel: NonEmptyStream[Int], f: (Int, Eval[Int]) => Eval[Int]) => + val got = nel.reduceRight(f).value + val last :: rev = nel.unwrap.toList.reverse + val expected = rev.reverse.foldRight(last)((a, b) => f(a, Now(b)).value) + got should ===(expected) + } + } + + test("reduce consistent with fold") { + forAll { (nel: NonEmptyStream[Int]) => + nel.reduce should ===(nel.fold) + } + } + + test("reduce consistent with reduceK") { + forAll { (nel: NonEmptyStream[Option[Int]]) => + nel.reduce(SemigroupK[Option].algebra[Int]) should ===(nel.reduceK) + } + } + + test("reduceLeftToOption consistent with foldLeft + Option") { + forAll { (nel: NonEmptyStream[Int], f: Int => String, g: (String, Int) => String) => + val expected = nel.tail.foldLeft(Option(f(nel.head))) { (opt, i) => + opt.map(s => g(s, i)) + } + nel.reduceLeftToOption(f)(g) should ===(expected) + } + } + + test("reduceRightToOption consistent with foldRight + Option") { + forAll { (nel: NonEmptyStream[Int], f: Int => String, g: (Int, Eval[String]) => Eval[String]) => + val got = nel.reduceRightToOption(f)(g).value + val last :: rev = nel.unwrap.toList.reverse + val expected = rev.reverse.foldRight(Option(f(last))) { (i, opt) => + opt.map(s => g(i, Now(s)).value) + } + got should ===(expected) + } + } + + test("filter includes elements based on a predicate") { + forAll { (nes: NonEmptyStream[Int], pred: Int => Boolean) => + nes.filter(pred) should ===(nes.unwrap.filter(pred)) + } + } + +} + +class ReducibleNonEmptyStreamSuite extends ReducibleSuite[NonEmptyStream]("NonEmptyStream") { + def iterator[T](nes: NonEmptyStream[T]): Iterator[T] = + (nes.head #:: nes.tail).iterator + + def range(start: Long, endInclusive: Long): NonEmptyStream[Long] = { + // 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) + } +} diff --git a/tests/src/test/scala/cats/tests/OneAndSuite.scala b/tests/src/test/scala/cats/tests/OneAndSuite.scala index d5bccdfe19..f15163837a 100644 --- a/tests/src/test/scala/cats/tests/OneAndSuite.scala +++ b/tests/src/test/scala/cats/tests/OneAndSuite.scala @@ -1,36 +1,15 @@ package cats package tests -import cats.kernel.laws.discipline.{EqTests, SemigroupTests} - -import cats.instances.stream._ -import cats.data.{NonEmptyStream, OneAnd} -import cats.laws.discipline.{ - ApplicativeTests, - ComonadTests, - FoldableTests, - FunctorTests, - MonadTests, - NonEmptyTraverseTests, - ReducibleTests, - SemigroupKTests, - SemigroupalTests, - SerializableTests, - TraverseTests -} +import cats.data.OneAnd +import cats.laws.discipline._ import cats.laws.discipline.arbitrary._ -import kernel.compat.lazyList._ -import compat.lazyList.toLazyList + class OneAndSuite extends CatsSuite { // Lots of collections here.. telling ScalaCheck to calm down a bit implicit override val generatorDrivenConfig: PropertyCheckConfiguration = PropertyCheckConfiguration(minSuccessful = 20, sizeRange = 5) - checkAll("OneAnd[Stream, Int]", EqTests[OneAnd[LazyList, Int]].eqv) - - checkAll("OneAnd[Stream, Int] with Option", - NonEmptyTraverseTests[OneAnd[LazyList, ?]].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option]) - checkAll("NonEmptyTraverse[OneAnd[Stream, A]]", SerializableTests.serializable(NonEmptyTraverse[OneAnd[LazyList, ?]])) { implicit val traverse = OneAnd.catsDataTraverseForOneAnd(ListWrapper.traverse) @@ -39,8 +18,6 @@ class OneAndSuite extends CatsSuite { checkAll("Traverse[OneAnd[ListWrapper, A]]", SerializableTests.serializable(Traverse[OneAnd[ListWrapper, ?]])) } - checkAll("OneAnd[Stream, Int]", ReducibleTests[OneAnd[LazyList, ?]].reducible[Option, Int, Int]) - checkAll("Reducible[OneAnd[Stream, ?]]", SerializableTests.serializable(Reducible[OneAnd[LazyList, ?]])) implicit val iso = SemigroupalTests.Isomorphisms .invariant[OneAnd[ListWrapper, ?]](OneAnd.catsDataFunctorForOneAnd(ListWrapper.functor)) @@ -68,9 +45,7 @@ class OneAndSuite extends CatsSuite { { implicit val alternative = ListWrapper.alternative checkAll("OneAnd[ListWrapper, Int]", SemigroupKTests[OneAnd[ListWrapper, ?]].semigroupK[Int]) - checkAll("OneAnd[Stream, Int]", SemigroupTests[OneAnd[LazyList, Int]].semigroup) checkAll("SemigroupK[OneAnd[ListWrapper, A]]", SerializableTests.serializable(SemigroupK[OneAnd[ListWrapper, ?]])) - checkAll("Semigroup[NonEmptyStream[Int]]", SerializableTests.serializable(Semigroup[OneAnd[LazyList, Int]])) } { @@ -79,153 +54,10 @@ class OneAndSuite extends CatsSuite { checkAll("Foldable[OneAnd[ListWrapper, A]]", SerializableTests.serializable(Foldable[OneAnd[ListWrapper, ?]])) } - { - // Test functor and subclasses don't have implicit conflicts - implicitly[Functor[NonEmptyStream]] - implicitly[Monad[NonEmptyStream]] - implicitly[Comonad[NonEmptyStream]] - } - - implicit val iso2 = SemigroupalTests.Isomorphisms.invariant[OneAnd[LazyList, ?]] - - //OneAnd's tailRecM fails on LazyList due to the fact that. todo: replace NonEmptyStream with NonEmptyLazyList using newtype https://github.com/typelevel/cats/issues/2903 - checkAll("NonEmptyStream[Int]", MonadTests[NonEmptyStream].stackUnsafeMonad[Int, Int, Int]) - checkAll("Monad[NonEmptyStream[A]]", SerializableTests.serializable(Monad[NonEmptyStream])) - - checkAll("NonEmptyStream[Int]", ComonadTests[NonEmptyStream].comonad[Int, Int, Int]) - checkAll("Comonad[NonEmptyStream[A]]", SerializableTests.serializable(Comonad[NonEmptyStream])) - test("size is consistent with toList.size") { forAll { (oa: OneAnd[Vector, Int]) => oa.size should ===(oa.toList.size.toLong) } } - test("Show is not empty and is formatted as expected") { - forAll { (nel: NonEmptyStream[Int]) => - nel.show.nonEmpty should ===(true) - nel.show.startsWith("OneAnd(") should ===(true) - nel.show should ===(implicitly[Show[NonEmptyStream[Int]]].show(nel)) - nel.show.contains(nel.head.show) should ===(true) - } - } - - test("Show is formatted correctly") { - val oneAnd = NonEmptyStream("Test") - oneAnd.show should ===(s"OneAnd(Test, ${compat.lazyList.lazyListString}())") - } - - test("Creating OneAnd + unwrap is identity") { - forAll { (i: Int, tail: LazyList[Int]) => - val stream = i #:: tail - val oneAnd = NonEmptyStream(i, tail: _*) - stream should ===(oneAnd.unwrap) - } - } - - test("NonEmptyStream#find is consistent with Stream#find") { - forAll { (nel: NonEmptyStream[Int], p: Int => Boolean) => - val stream = nel.unwrap - nel.find(p) should ===(stream.find(p)) - } - } - - test("NonEmptyStream#exists is consistent with Stream#exists") { - forAll { (nel: NonEmptyStream[Int], p: Int => Boolean) => - val stream = nel.unwrap - nel.exists(p) should ===(stream.exists(p)) - } - } - - test("NonEmptyStream#forall is consistent with Stream#forall") { - forAll { (nel: NonEmptyStream[Int], p: Int => Boolean) => - val stream = nel.unwrap - nel.forall(p) should ===(stream.forall(p)) - } - } - - test("NonEmptyStream#map is consistent with Stream#map") { - forAll { (nel: NonEmptyStream[Int], p: Int => String) => - val stream = nel.unwrap - nel.map(p).unwrap should ===(stream.map(p)) - } - } - - test("NonEmptyStream#nonEmptyPartition remains sorted") { - forAll { (nes: NonEmptyStream[Int], f: Int => Either[String, String]) => - val nesf = nes.map(f) - val sortedStream = (nesf.head #:: nesf.tail).sorted - val sortedNes = OneAnd(sortedStream.head, sortedStream.tail) - val ior = Reducible[NonEmptyStream].nonEmptyPartition(sortedNes)(identity) - - ior.left.map(xs => xs.sorted should ===(xs)) - ior.right.map(xs => xs.sorted should ===(xs)) - } - } - - test("reduceLeft consistent with foldLeft") { - forAll { (nel: NonEmptyStream[Int], f: (Int, Int) => Int) => - nel.reduceLeft(f) should ===(nel.tail.foldLeft(nel.head)(f)) - } - } - - test("reduceRight consistent with foldRight") { - forAll { (nel: NonEmptyStream[Int], f: (Int, Eval[Int]) => Eval[Int]) => - val got = nel.reduceRight(f).value - val last :: rev = nel.unwrap.toList.reverse - val expected = rev.reverse.foldRight(last)((a, b) => f(a, Now(b)).value) - got should ===(expected) - } - } - - test("reduce consistent with fold") { - forAll { (nel: NonEmptyStream[Int]) => - nel.reduce should ===(nel.fold) - } - } - - test("reduce consistent with reduceK") { - forAll { (nel: NonEmptyStream[Option[Int]]) => - nel.reduce(SemigroupK[Option].algebra[Int]) should ===(nel.reduceK) - } - } - - test("reduceLeftToOption consistent with foldLeft + Option") { - forAll { (nel: NonEmptyStream[Int], f: Int => String, g: (String, Int) => String) => - val expected = nel.tail.foldLeft(Option(f(nel.head))) { (opt, i) => - opt.map(s => g(s, i)) - } - nel.reduceLeftToOption(f)(g) should ===(expected) - } - } - - test("reduceRightToOption consistent with foldRight + Option") { - forAll { (nel: NonEmptyStream[Int], f: Int => String, g: (Int, Eval[String]) => Eval[String]) => - val got = nel.reduceRightToOption(f)(g).value - val last :: rev = nel.unwrap.toList.reverse - val expected = rev.reverse.foldRight(Option(f(last))) { (i, opt) => - opt.map(s => g(i, Now(s)).value) - } - got should ===(expected) - } - } - - test("filter includes elements based on a predicate") { - forAll { (nes: NonEmptyStream[Int], pred: Int => Boolean) => - nes.filter(pred) should ===(nes.unwrap.filter(pred)) - } - } - -} - -class ReducibleNonEmptyStreamSuite extends ReducibleSuite[NonEmptyStream]("NonEmptyStream") { - def iterator[T](nes: NonEmptyStream[T]): Iterator[T] = - (nes.head #:: nes.tail).iterator - - def range(start: Long, endInclusive: Long): NonEmptyStream[Long] = { - // if we inline this we get a bewildering implicit numeric widening - // error message in Scala 2.10 - val tailStart: Long = start + 1L - NonEmptyStream(start, toLazyList(tailStart.to(endInclusive))) - } } From 8a2432c62df978787d44d7987dcf02a688f68e3d Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Thu, 20 Jun 2019 22:54:46 -0400 Subject: [PATCH 02/12] reformat --- .../cats/tests/NonEmptyStreamSuite.scala | 6 +- .../test/scala/cats/tests/OneAndSuite.scala | 62 ------------------- 2 files changed, 1 insertion(+), 67 deletions(-) diff --git a/tests/src/test/scala-2.12-/cats/tests/NonEmptyStreamSuite.scala b/tests/src/test/scala-2.12-/cats/tests/NonEmptyStreamSuite.scala index 2be2d3e85e..9cb8d7874b 100644 --- a/tests/src/test/scala-2.12-/cats/tests/NonEmptyStreamSuite.scala +++ b/tests/src/test/scala-2.12-/cats/tests/NonEmptyStreamSuite.scala @@ -13,21 +13,17 @@ class NonEmptyStreamSuite extends CatsSuite { PropertyCheckConfiguration(minSuccessful = 20, sizeRange = 5) checkAll("NonEmptyStream[Int]", EqTests[NonEmptyStream[Int]].eqv) - + checkAll("NonEmptyStream[Int] with Option", NonEmptyTraverseTests[NonEmptyStream].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option]) checkAll("NonEmptyTraverse[NonEmptyStream[A]]", SerializableTests.serializable(NonEmptyTraverse[NonEmptyStream[?]])) - checkAll("NonEmptyStream[Int]", ReducibleTests[NonEmptyStream].reducible[Option, Int, Int]) checkAll("Reducible[NonEmptyStream]", SerializableTests.serializable(Reducible[NonEmptyStream])) - - checkAll("NonEmptyStream[Int]", SemigroupTests[NonEmptyStream[Int]].semigroup) checkAll("Semigroup[NonEmptyStream[Int]]", SerializableTests.serializable(Semigroup[NonEmptyStream[Int]])) - { // Test functor and subclasses don't have implicit conflicts implicitly[Functor[NonEmptyStream]] diff --git a/tests/src/test/scala/cats/tests/OneAndSuite.scala b/tests/src/test/scala/cats/tests/OneAndSuite.scala index f15163837a..8b13789179 100644 --- a/tests/src/test/scala/cats/tests/OneAndSuite.scala +++ b/tests/src/test/scala/cats/tests/OneAndSuite.scala @@ -1,63 +1 @@ -package cats -package tests -import cats.data.OneAnd -import cats.laws.discipline._ -import cats.laws.discipline.arbitrary._ - -class OneAndSuite extends CatsSuite { - // Lots of collections here.. telling ScalaCheck to calm down a bit - implicit override val generatorDrivenConfig: PropertyCheckConfiguration = - PropertyCheckConfiguration(minSuccessful = 20, sizeRange = 5) - - - { - implicit val traverse = OneAnd.catsDataTraverseForOneAnd(ListWrapper.traverse) - checkAll("OneAnd[ListWrapper, Int] with Option", - TraverseTests[OneAnd[ListWrapper, ?]].traverse[Int, Int, Int, Int, Option, Option]) - checkAll("Traverse[OneAnd[ListWrapper, A]]", SerializableTests.serializable(Traverse[OneAnd[ListWrapper, ?]])) - } - - - implicit val iso = SemigroupalTests.Isomorphisms - .invariant[OneAnd[ListWrapper, ?]](OneAnd.catsDataFunctorForOneAnd(ListWrapper.functor)) - - // Test instances that have more general constraints - { - implicit val monad = ListWrapper.monad - implicit val alt = ListWrapper.alternative - checkAll("OneAnd[ListWrapper, Int]", MonadTests[OneAnd[ListWrapper, ?]].monad[Int, Int, Int]) - checkAll("MonadTests[OneAnd[ListWrapper, A]]", SerializableTests.serializable(Monad[OneAnd[ListWrapper, ?]])) - } - - { - implicit val alternative = ListWrapper.alternative - checkAll("OneAnd[ListWrapper, Int]", ApplicativeTests[OneAnd[ListWrapper, ?]].applicative[Int, Int, Int]) - checkAll("Applicative[OneAnd[ListWrapper, A]]", SerializableTests.serializable(Applicative[OneAnd[ListWrapper, ?]])) - } - - { - implicit val functor = ListWrapper.functor - checkAll("OneAnd[ListWrapper, Int]", FunctorTests[OneAnd[ListWrapper, ?]].functor[Int, Int, Int]) - checkAll("Functor[OneAnd[ListWrapper, A]]", SerializableTests.serializable(Functor[OneAnd[ListWrapper, ?]])) - } - - { - implicit val alternative = ListWrapper.alternative - checkAll("OneAnd[ListWrapper, Int]", SemigroupKTests[OneAnd[ListWrapper, ?]].semigroupK[Int]) - checkAll("SemigroupK[OneAnd[ListWrapper, A]]", SerializableTests.serializable(SemigroupK[OneAnd[ListWrapper, ?]])) - } - - { - implicit val foldable = ListWrapper.foldable - checkAll("OneAnd[ListWrapper, Int]", FoldableTests[OneAnd[ListWrapper, ?]].foldable[Int, Int]) - checkAll("Foldable[OneAnd[ListWrapper, A]]", SerializableTests.serializable(Foldable[OneAnd[ListWrapper, ?]])) - } - - test("size is consistent with toList.size") { - forAll { (oa: OneAnd[Vector, Int]) => - oa.size should ===(oa.toList.size.toLong) - } - } - -} From 3f1264685875034cf9e241ce0604cb1c366c0b28 Mon Sep 17 00:00:00 2001 From: Nathaniel May Date: Thu, 27 Jun 2019 11:25:46 -0400 Subject: [PATCH 03/12] partially converted NonEmptyLazyList from NonEmptyList --- .../cats/data/NonEmptyLazyList.scala | 682 ++++++++++++++++++ 1 file changed, 682 insertions(+) create mode 100644 core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala diff --git a/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala new file mode 100644 index 0000000000..59d803b320 --- /dev/null +++ b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala @@ -0,0 +1,682 @@ +package cats.data + +import cats.data.NonEmptyLazyList.ZipNonEmptyLazyList +import cats.instances.lazyList._ +import cats.kernel.compat.scalaVersionSpecific.LazyList +import cats.syntax.order._ + +import scala.annotation.tailrec +import scala.collection.immutable.{SortedMap, TreeMap, TreeSet} +import scala.collection.mutable +import scala.collection.mutable.ListBuffer + +/** + * A data type which represents a non empty lazy list of A, with + * single element (head) and optional structure (tail). + */ +// TODO *** considering implementing as non-case class so that head can be call-by-name +final case class NonEmptyLazyList[+A](head: A, tail: LazyList[A]) { + + /** + * Return the head and tail into a single list + * {{{ + * scala> import cats.data.NonEmptyLazyList + * scala> val nell = NonEmptyLazyList.of(1, 2, 3, 4, 5) + * scala> nell.toLazyList + * res0: scala.collection.immutable.LazyList[Int] = LazyList(1, 2, 3, 4, 5) + * }}} + */ + def toLazyList: LazyList[A] = head #:: tail + + /** + * Selects the last element + * {{{ + * scala> import cats.data.NonEmptyLazyList + * scala> val nell = NonEmptyLazyList.of(1, 2, 3, 4, 5) + * scala> nell.last + * res0: Int = 5 + * }}} + */ + def last: A = tail.lastOption match { + case None => head + case Some(a) => a + } + + /** + * Selects all elements except the last + * + * {{{ + * scala> import cats.data.NonEmptyLazyList + * scala> val nell = NonEmptyLazyList.of(1, 2, 3, 4, 5) + * scala> nell.init + * res0: scala.collection.immutable.LazyList[Int] = LazyList(1, 2, 3, 4) + * }}} + */ + def init: LazyList[A] = tail match { + case Nil => LazyList.empty + case t => head #:: t.init + } + + /** + * The size of this NonEmptyLazyList + * + * {{{ + * scala> import cats.data.NonEmptyLazyList + * scala> val nell = NonEmptyLazyList.of(1, 2, 3, 4, 5) + * scala> nell.size + * res0: Int = 5 + * }}} + */ + def size: Int = 1 + tail.size + + def length: Int = size + + /** + * Applies f to all the elements of the structure + */ + def map[B](f: A => B): NonEmptyLazyList[B] = + NonEmptyLazyList(f(head), tail.map(f)) + + def ++[AA >: A](l: LazyList[AA]): NonEmptyLazyList[AA] = + concat(l) + + def concat[AA >: A](other: LazyList[AA]): NonEmptyLazyList[AA] = + NonEmptyLazyList(head, tail #::: other) + + /** + * Append another NonEmptyLazyList + */ + def concatNel[AA >: A](other: NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = + NonEmptyLazyList(head, tail #::: other.toLazyList) + + def flatMap[B](f: A => NonEmptyLazyList[B]): NonEmptyLazyList[B] = + f(head) ++ tail.flatMap(f.andThen(_.toLazyList)) + + def #::[AA >: A](a: AA): NonEmptyLazyList[AA] = + prepend(a) + + def prepend[AA >: A](a: AA): NonEmptyLazyList[AA] = + NonEmptyLazyList(a, head #:: tail) + + /** + * Alias for append + * + * {{{ + * scala> import cats.data.NonEmptyLazyList + * scala> val nell = NonEmptyLazyList.of(1, 2, 3) + * scala> nell :+ 4 + * res0: cats.data.NonEmptyLazyList[Int] = NonEmptyLazyList(1, 2, 3, 4) + * }}} + */ + def :+[AA >: A](a: AA): NonEmptyLazyList[AA] = + append(a) + + def append[AA >: A](a: AA): NonEmptyLazyList[AA] = + NonEmptyLazyList(head, tail :+ a) + + /** + * Alias for concatNell + * + * {{{ + * scala> import cats.data.NonEmptyLazyList + * scala> val nell = NonEmptyLazyList.of(1, 2, 3) + * scala> nell #::: NonEmptyLazyList.of(4, 5) + * res0: cats.data.NonEmptyLazyList[Int] = NonEmptyLazyList(1, 2, 3, 4, 5) + * }}} + */ + def #:::[AA >: A](other: NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = + other.concatNel(this) + + /** + * Remove elements not matching the predicate + * + * {{{ + * scala> import cats.data.NonEmptyLazyList + * scala> val nell = NonEmptyLazyList.of(1, 2, 3, 4, 5) + * scala> nell.filter(_ < 3) + * res0: scala.collection.immutable.LazyList[Int] = LazyList(1, 2) + * }}} + */ + def filter(p: A => Boolean): LazyList[A] = { + val ftail = tail.filter(p) + if (p(head)) head #:: ftail + else ftail + } + + /** + * Remove elements matching the predicate + * + * {{{ + * scala> import cats.data.NonEmptyLazyList + * scala> val nell = NonEmptyLazyList.of(1, 2, 3, 4, 5) + * scala> nell.filterNot(_ < 3) + * res0: scala.collection.immutable.LazyList[Int] = LazyList(3, 4, 5) + * }}} + */ + def filterNot(p: A => Boolean): LazyList[A] = { + val ftail = tail.filterNot(p) + if (p(head)) ftail + else head #:: ftail + } + + /** + * Builds a new `LazyList` by applying a partial function to + * all the elements from this `NonEmptyLazyList` on which the function is defined + * + * {{{ + * scala> import cats.data.NonEmptyLazyList + * scala> val nell = NonEmptyLazyList.of(1, 2, 3, 4, 5) + * scala> nell.collect { case v if v < 3 => v } + * res0: scala.collection.immutable.LazyList[Int] = LazyList(1, 2) + * scala> nell.collect { + * | case v if v % 2 == 0 => "even" + * | case _ => "odd" + * | } + * res1: scala.collection.immutable.LazyList[String] = LazyList(odd, even, odd, even, odd) + * }}} + */ + def collect[B](pf: PartialFunction[A, B]): LazyList[B] = + if (pf.isDefinedAt(head)) { + pf.apply(head) #:: tail.collect(pf) + } else { + tail.collect(pf) + } + + /** + * Find the first element matching the predicate, if one exists + */ + def find(p: A => Boolean): Option[A] = + if (p(head)) Some(head) + else tail.find(p) + + /** + * Check whether at least one element satisfies the predicate + */ + def exists(p: A => Boolean): Boolean = + p(head) || tail.exists(p) + + /** + * Check whether all elements satisfy the predicate + */ + def forall(p: A => Boolean): Boolean = + p(head) && tail.forall(p) + + /** + * Left-associative fold on the structure using f. + */ + def foldLeft[B](b: B)(f: (B, A) => B): B = + tail.foldLeft(f(b, head))(f) + + /** + * Right-associative fold on the structure using f. + */ + def foldRight[B](lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = + Foldable[LazyList].foldRight(toLazyList, lb)(f) + + /** + * Left-associative reduce using f. + */ + def reduceLeft[AA >: A](f: (AA, AA) => AA): AA = + tail.foldLeft[AA](head)(f) + + /** + * Reduce using the `Semigroup` of `AA`. + */ + def reduce[AA >: A](implicit S: Semigroup[AA]): AA = + S.combineAllOption(toLazyList).get + + def traverse[G[_], B](f: A => G[B])(implicit G: Applicative[G]): G[NonEmptyLazyList[B]] = + G.map2Eval(f(head), Always(Traverse[List].traverse(tail)(f)))(NonEmptyLazyList(_, _)).value + + // TODO *** deal with ListBuffer + def coflatMap[B](f: NonEmptyLazyList[A] => B): NonEmptyLazyList[B] = { + val buf = ListBuffer.empty[B] + @tailrec def consume(as: List[A]): List[B] = + as match { + case Nil => buf.toList + case a #:: as => + buf += f(NonEmptyLazyList(a, as)) + consume(as) + } + NonEmptyLazyList(f(this), consume(tail)) + } + + def ===[AA >: A](o: NonEmptyLazyList[AA])(implicit AA: Eq[AA]): Boolean = + ((this.head: AA) === o.head) && (this.tail: LazyList[AA]) === o.tail + + def show[AA >: A](implicit AA: Show[AA]): String = + toLazyList.iterator.map(AA.show).mkString("NonEmptyLazyList(", ", ", ")") + + override def toString: String = s"NonEmpty$toLazyList" + + /** + * Remove duplicates. Duplicates are checked using `Order[_]` instance. + */ + //TODO *** deal with ListBuffer + def distinct[AA >: A](implicit O: Order[AA]): NonEmptyLazyList[AA] = { + implicit val ord = O.toOrdering + + val buf = ListBuffer.empty[AA] + tail.foldLeft(TreeSet(head: AA)) { (elementsSoFar, b) => + if (elementsSoFar(b)) elementsSoFar + else { + buf += b; elementsSoFar + b + } + } + + NonEmptyLazyList(head, buf.toList) + } + + /** + * Reverse this `NonEmptyLazyList`. + * + * {{{ + * scala> import cats.data.NonEmptyLazyList + * scala> val nell = NonEmptyLazyList.of(1, 2, 3) + * scala> nell.reverse + * res0: cats.data.NonEmptyLazyList[Int] = NonEmptyLazyList(3, 2, 1) + * }}} + */ + def reverse: NonEmptyLazyList[A] = { + @tailrec + def go(h: A, rest: LazyList[A], acc: LazyList[A]): NonEmptyLazyList[A] = + rest match { + case Nil => NonEmptyLazyList(h, acc) + case h1 #:: t1 => go(h1, t1, h #:: acc) + } + go(head, tail, Nil) + } + + /** + * Zips this `NonEmptyLazyList` with another `NonEmptyLazyList` and applies a function for each pair of elements. + * + * {{{ + * scala> import cats.data.NonEmptyLazyList + * scala> val as = NonEmptyLazyList.of(1, 2, 3) + * scala> val bs = NonEmptyLazyList.of("A", "B", "C") + * scala> as.zipWith(bs)(_ + _) + * res0: cats.data.NonEmptyLazyList[String] = NonEmptyLazyList(1A, 2B, 3C) + * }}} + */ + def zipWith[B, C](b: NonEmptyLazyList[B])(f: (A, B) => C): NonEmptyLazyList[C] = { + // TODO *** go back and find all the Nils and make them empty + @tailrec + def zwRev(as: LazyList[A], bs: LazyList[B], acc: LazyList[C]): LazyList[C] = (as, bs) match { + case (Nil, _) => acc + case (_, Nil) => acc + case (x #:: xs, y #:: ys) => zwRev(xs, ys, f(x, y) #:: acc) + } + + NonEmptyLazyList(f(head, b.head), zwRev(tail, b.tail, Nil).reverse) + } + + /** + * Zips each element of this `NonEmptyLazyList` with its index. + * + * {{{ + * scala> import cats.data.NonEmptyLazyList + * scala> val nell = NonEmptyLazyList.of("a", "b", "c") + * scala> nell.zipWithIndex + * res0: cats.data.NonEmptyLazyList[(String, Int)] = NonEmptyLazyList((a,0), (b,1), (c,2)) + * }}} + */ // TODO *** List builder + def zipWithIndex: NonEmptyLazyList[(A, Int)] = { + val bldr = List.newBuilder[(A, Int)] + var idx = 1 + val it = tail.iterator + while (it.hasNext) { + bldr += ((it.next, idx)) + idx += 1 + } + NonEmptyLazyList((head, 0), bldr.result) + } + + /** + * Sorts this `NonEmptyLazyList` according to an `Order` on transformed `B` from `A` + * + * {{{ + * scala> import cats.data.NonEmptyLazyList + * scala> import cats.instances.int._ + * scala> val nell = NonEmptyLazyList.of(('a', 4), ('z', 1), ('e', 22)) + * scala> nell.sortBy(_._2) + * res0: cats.data.NonEmptyLazyList[(Char, Int)] = NonEmptyLazyList((z,1), (a,4), (e,22)) + * }}} + */ + def sortBy[B](f: A => B)(implicit B: Order[B]): NonEmptyLazyList[A] = + // safe: sorting a NonEmptyLazyList cannot produce an empty LazyList + NonEmptyLazyList.fromLazyListUnsafe(toLazyList.sortBy(f)(B.toOrdering)) + + /** + * Sorts this `NonEmptyLazyList` according to an `Order` + * + * {{{ + * scala> import cats.data.NonEmptyLazyList + * scala> import cats.instances.int._ + * scala> val nell = NonEmptyLazyList.of(12, 4, 3, 9) + * scala> nell.sorted + * res0: cats.data.NonEmptyLazyList[Int] = NonEmptyLazyList(3, 4, 9, 12) + * }}} + */ + def sorted[AA >: A](implicit AA: Order[AA]): NonEmptyLazyList[AA] = + // safe: sorting a NonEmptyLazyList cannot produce an empty LazyList + NonEmptyLazyList.fromLazyListUnsafe(toLazyList.sorted(AA.toOrdering)) + + /** + * Groups elements inside this `NonEmptyLazyList` according to the `Order` + * of the keys produced by the given mapping function. + * + * {{{ + * scala> import scala.collection.immutable.SortedMap + * scala> import cats.data.NonEmptyLazyList + * scala> import cats.instances.boolean._ + * scala> val nell = NonEmptyLazyList.of(12, -2, 3, -5) + * scala> nell.groupBy(_ >= 0) + * res0: SortedMap[Boolean, cats.data.NonEmptyLazyList[Int]] = Map(false -> NonEmptyLazyList(-2, -5), true -> NonEmptyLazyList(12, 3)) + * }}} + */ // TODO builder + def groupBy[B](f: A => B)(implicit B: Order[B]): SortedMap[B, NonEmptyLazyList[A]] = { + implicit val ordering: Ordering[B] = B.toOrdering + var m = TreeMap.empty[B, mutable.Builder[A, LazyList[A]]] + + for { elem <- toLazyList } { + val k = f(elem) + + m.get(k) match { + case None => m += ((k, List.newBuilder[A] += elem)) + case Some(builder) => builder += elem + } + } + + m.map { + case (k, v) => (k, NonEmptyLazyList.fromListUnsafe(v.result)) + }: TreeMap[B, NonEmptyLazyList[A]] + } + + /** + * Groups elements inside this `NonEmptyLazyList` according to the `Order` + * of the keys produced by the given mapping function. + * + * {{{ + * scala> import cats.data._ + * scala> import cats.instances.boolean._ + * scala> val nell = NonEmptyLazyList.of(12, -2, 3, -5) + * scala> nell.groupByNem(_ >= 0) + * res0: NonEmptyMap[Boolean, NonEmptyLazyList[Int]] = Map(false -> NonEmptyLazyList(-2, -5), true -> NonEmptyLazyList(12, 3)) + * }}} + */ + def groupByNem[B](f: A => B)(implicit B: Order[B]): NonEmptyMap[B, NonEmptyLazyList[A]] = + NonEmptyMap.fromMapUnsafe(groupBy(f)) + + /** + * Creates new `NonEmptyMap`, similarly to LazyList#toMap from scala standard library. + *{{{ + * scala> import cats.data._ + * scala> import cats.instances.int._ + * scala> val nell = NonEmptyLazyList((0, "a"), LazyList((1, "b"),(0, "c"), (2, "d"))) + * scala> nell.toNem + * res0: NonEmptyMap[Int,String] = Map(0 -> c, 1 -> b, 2 -> d) + *}}} + * + */ // TODO *** not lazy. probably ok though + def toNem[T, U](implicit ev: A <:< (T, U), order: Order[T]): NonEmptyMap[T, U] = + NonEmptyMap.fromMapUnsafe(SortedMap(toLazyList.map(ev): _*)(order.toOrdering)) + + /** + * Creates new `NonEmptySet`, similarly to LazyList#toSet from scala standard library. + *{{{ + * scala> import cats.data._ + * scala> import cats.instances.int._ + * scala> val nell = NonEmptyLazyList(1, LazyList(2,2,3,4)) + * scala> nell.toNes + * res0: cats.data.NonEmptySet[Int] = TreeSet(1, 2, 3, 4) + *}}} + */ + def toNes[B >: A](implicit order: Order[B]): NonEmptySet[B] = + NonEmptySet.of(head, tail: _*) +} + +object NonEmptyLazyList extends NonEmptyLazyListInstances { + + def of[A](head: A, tail: A*): NonEmptyLazyList[A] = NonEmptyLazyList(head, tail.toLazyList) + + def ofInitLast[A](init: LazyList[A], last: A): NonEmptyLazyList[A] = + init match { + case Nil => NonEmptyLazyList(last, Nil) + case h #:: t => NonEmptyLazyList(h, t :+ last) + } + + def one[A](head: A): NonEmptyLazyList[A] = NonEmptyLazyList(head, Nil) + + /** + * Create a `NonEmptyLazyList` from a `LazyList`. + * + * The result will be `None` if the input list is empty and `Some` wrapping a + * `NonEmptyLazyList` otherwise. + * + * @see [[fromListUnsafe]] for an unsafe version that throws an exception if + * the input list is empty. + */ + def fromLazyList[A](l: LazyList[A]): Option[NonEmptyLazyList[A]] = + l match { + case Nil => None + case h #:: t => Some(NonEmptyLazyList(h, t)) + } + + /** + * Create a `NonEmptyLazyList` from a `LazyList`, or throw an + * `IllegalArgumentException` if the input list is empty. + * + * @see [[fromLazyList]] for a safe version that returns `None` if the input list + * is empty. + */ + def fromLazyListUnsafe[A](l: LazyList[A]): NonEmptyLazyList[A] = + l match { + case Nil => throw new IllegalArgumentException("Cannot create NonEmptyLazyList from empty LazyList") + case h #:: t => NonEmptyLazyList(h, t) + } + + // TODO *** toList + def fromFoldable[F[_], A](fa: F[A])(implicit F: Foldable[F]): Option[NonEmptyLazyList[A]] = + fromLazyList(F.toList(fa)) + + def fromReducible[F[_], A](fa: F[A])(implicit F: Reducible[F]): NonEmptyLazyList[A] = + F.toNonEmptyLazyList(fa) + + class ZipNonEmptyLazyList[A](val value: NonEmptyLazyList[A]) extends AnyVal + + object ZipNonEmptyLazyList { + + def apply[A](nev: NonEmptyLazyList[A]): ZipNonEmptyLazyList[A] = + new ZipNonEmptyLazyList(nev) + + 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 zipNelEq[A: Eq]: Eq[ZipNonEmptyLazyList[A]] = Eq.by(_.value) + } +} + +sealed abstract private[data] class NonEmptyLazyListInstances extends NonEmptyLazyListInstances0 { + + implicit val catsDataInstancesForNonEmptyLazyList: SemigroupK[NonEmptyLazyList] + with Reducible[NonEmptyLazyList] + with Bimonad[NonEmptyLazyList] + with NonEmptyTraverse[NonEmptyLazyList] = + new NonEmptyReducible[NonEmptyLazyList, LazyList] with SemigroupK[NonEmptyLazyList] with Bimonad[NonEmptyLazyList] + with NonEmptyTraverse[NonEmptyLazyList] { + + def combineK[A](a: NonEmptyLazyList[A], b: NonEmptyLazyList[A]): NonEmptyLazyList[A] = + a.concatNel(b) + + override def split[A](fa: NonEmptyLazyList[A]): (A, LazyList[A]) = (fa.head, fa.tail) + + override def reduceLeft[A](fa: NonEmptyLazyList[A])(f: (A, A) => A): A = + fa.reduceLeft(f) + + override def reduce[A](fa: NonEmptyLazyList[A])(implicit A: Semigroup[A]): A = + fa.reduce + + override def map[A, B](fa: NonEmptyLazyList[A])(f: A => B): NonEmptyLazyList[B] = + fa.map(f) + + def pure[A](x: A): NonEmptyLazyList[A] = + NonEmptyLazyList.one(x) + + def flatMap[A, B](fa: NonEmptyLazyList[A])(f: A => NonEmptyLazyList[B]): NonEmptyLazyList[B] = + fa.flatMap(f) + + def coflatMap[A, B](fa: NonEmptyLazyList[A])(f: NonEmptyLazyList[A] => B): NonEmptyLazyList[B] = + fa.coflatMap(f) + + def extract[A](fa: NonEmptyLazyList[A]): A = fa.head + + def nonEmptyTraverse[G[_], A, B](nell: NonEmptyLazyList[A])(f: A => G[B])(implicit G: Apply[G]): G[NonEmptyLazyList[B]] = + Foldable[List] + .reduceRightToOption[A, G[List[B]]](nell.tail)(a => G.map(f(a))(_ #:: Nil)) { (a, lglb) => + G.map2Eval(f(a), lglb)(_ #:: _) + } + .map { + case None => G.map(f(nell.head))(NonEmptyLazyList(_, Nil)) + case Some(gtail) => G.map2(f(nell.head), gtail)(NonEmptyLazyList(_, _)) + } + .value + + override def traverse[G[_], A, B](fa: NonEmptyLazyList[A])(f: A => G[B])(implicit G: Applicative[G]): G[NonEmptyLazyList[B]] = + fa.traverse(f) + + override def foldLeft[A, B](fa: NonEmptyLazyList[A], b: B)(f: (B, A) => B): B = + fa.foldLeft(b)(f) + + override def foldRight[A, B](fa: NonEmptyLazyList[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = + fa.foldRight(lb)(f) + + override def foldMap[A, B](fa: NonEmptyLazyList[A])(f: A => B)(implicit B: Monoid[B]): B = + B.combineAll(fa.toLazyList.iterator.map(f)) + + // TODO *** list buffer + def tailRecM[A, B](a: A)(f: A => NonEmptyLazyList[Either[A, B]]): NonEmptyLazyList[B] = { + val buf = new ListBuffer[B] + @tailrec def go(v: NonEmptyLazyList[Either[A, B]]): Unit = v.head match { + case Right(b) => + buf += b + NonEmptyLazyList.fromList(v.tail) match { + case Some(t) => go(t) + case None => () + } + case Left(a) => go(f(a) ++ v.tail) + } + go(f(a)) + NonEmptyLazyList.fromListUnsafe(buf.result()) + } + + override def fold[A](fa: NonEmptyLazyList[A])(implicit A: Monoid[A]): A = + fa.reduce + + override def nonEmptyPartition[A, B, C](fa: NonEmptyLazyList[A])(f: (A) => Either[B, C]): Ior[NonEmptyLazyList[B], NonEmptyLazyList[C]] = { + import cats.syntax.either._ + + val reversed = fa.reverse + val lastIor = f(reversed.head).bimap(NonEmptyLazyList.one, NonEmptyLazyList.one).toIor + + reversed.tail.foldLeft(lastIor)( + (ior, a) => + (f(a), ior) match { + case (Right(c), Ior.Left(_)) => ior.putRight(NonEmptyLazyList.one(c)) + case (Right(c), _) => ior.map(c #:: _) + case (Left(b), Ior.Right(r)) => Ior.bothNel(b, r) + case (Left(b), _) => ior.leftMap(b #:: _) + } + ) + + } + + override def find[A](fa: NonEmptyLazyList[A])(f: A => Boolean): Option[A] = + fa.find(f) + + override def forall[A](fa: NonEmptyLazyList[A])(p: A => Boolean): Boolean = + fa.forall(p) + + override def exists[A](fa: NonEmptyLazyList[A])(p: A => Boolean): Boolean = + fa.exists(p) + + // TODO *** not sure what to do about this + override def toList[A](fa: NonEmptyLazyList[A]): List[A] = fa.toLazyList.toList + + override def toNonEmptyLazyList[A](fa: NonEmptyLazyList[A]): NonEmptyLazyList[A] = fa + + override def get[A](fa: NonEmptyLazyList[A])(idx: Long): Option[A] = + if (idx == 0) Some(fa.head) else Foldable[List].get(fa.tail)(idx - 1) + } + + implicit def catsDataShowForNonEmptyLazyList[A](implicit A: Show[A]): Show[NonEmptyLazyList[A]] = + Show.show[NonEmptyLazyList[A]](_.show) + + implicit def catsDataSemigroupForNonEmptyLazyList[A]: Semigroup[NonEmptyLazyList[A]] = + SemigroupK[NonEmptyLazyList].algebra[A] + + implicit def catsDataOrderForNonEmptyLazyList[A](implicit A: Order[A]): Order[NonEmptyLazyList[A]] = + new NonEmptyLazyListOrder[A] { + val A0 = A + } + + implicit def catsDataNonEmptyParallelForNonEmptyLazyList[A]: NonEmptyParallel[NonEmptyLazyList, ZipNonEmptyLazyList] = + new NonEmptyParallel[NonEmptyLazyList, ZipNonEmptyLazyList] { + + def flatMap: FlatMap[NonEmptyLazyList] = NonEmptyLazyList.catsDataInstancesForNonEmptyLazyList + + def apply: Apply[ZipNonEmptyLazyList] = ZipNonEmptyLazyList.catsDataCommutativeApplyForZipNonEmptyLazyList + + def sequential: ZipNonEmptyLazyList ~> NonEmptyLazyList = + λ[ZipNonEmptyLazyList ~> NonEmptyLazyList](_.value) + + def parallel: NonEmptyLazyList ~> ZipNonEmptyLazyList = + λ[NonEmptyLazyList ~> ZipNonEmptyLazyList](nell => new ZipNonEmptyLazyList(nell)) + } +} + +sealed abstract private[data] class NonEmptyLazyListInstances0 extends NonEmptyLazyListInstances1 { + implicit def catsDataPartialOrderForNonEmptyLazyList[A](implicit A: PartialOrder[A]): PartialOrder[NonEmptyLazyList[A]] = + new NonEmptyLazyListPartialOrder[A] { + val A0 = A + } +} + +sealed abstract private[data] class NonEmptyLazyListInstances1 { + + implicit def catsDataEqForNonEmptyLazyList[A](implicit A: Eq[A]): Eq[NonEmptyLazyList[A]] = + new NonEmptyLazyListEq[A] { + val A0 = A + } +} + +sealed private[data] trait NonEmptyLazyListEq[A] extends Eq[NonEmptyLazyList[A]] { + implicit def A0: Eq[A] + + override def eqv(x: NonEmptyLazyList[A], y: NonEmptyLazyList[A]): Boolean = x === y +} + +sealed private[data] trait NonEmptyLazyListPartialOrder[A] extends PartialOrder[NonEmptyLazyList[A]] with NonEmptyLazyListEq[A] { + implicit override def A0: PartialOrder[A] + + override def partialCompare(x: NonEmptyLazyList[A], y: NonEmptyLazyList[A]): Double = + x.toLazyList.partialCompare(y.toLazyList) +} + +sealed abstract private[data] class NonEmptyLazyListOrder[A] + extends Order[NonEmptyLazyList[A]] + with NonEmptyLazyListPartialOrder[A] { + implicit override def A0: Order[A] + + override def compare(x: NonEmptyLazyList[A], y: NonEmptyLazyList[A]): Int = + x.toLazyList.compare(y.toLazyList) +} + From e719b193dd38b1a9ee0df7598ae84bc2c35df8c5 Mon Sep 17 00:00:00 2001 From: Nathaniel May Date: Thu, 27 Jun 2019 12:21:36 -0400 Subject: [PATCH 04/12] better TODOs and replaced occurrances of Nil --- .../cats/data/NonEmptyLazyList.scala | 62 +++++++++---------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala index 59d803b320..55ba132ed7 100644 --- a/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala +++ b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala @@ -2,7 +2,6 @@ package cats.data import cats.data.NonEmptyLazyList.ZipNonEmptyLazyList import cats.instances.lazyList._ -import cats.kernel.compat.scalaVersionSpecific.LazyList import cats.syntax.order._ import scala.annotation.tailrec @@ -10,13 +9,14 @@ import scala.collection.immutable.{SortedMap, TreeMap, TreeSet} import scala.collection.mutable import scala.collection.mutable.ListBuffer +// TODO *** will handle mutable builders later - input appreciated +// TODO *** 2.13 compiler reports matching empty cases on LazyList() - not Empty because of new `State` impl. Better way? /** * A data type which represents a non empty lazy list of A, with * single element (head) and optional structure (tail). */ -// TODO *** considering implementing as non-case class so that head can be call-by-name +// TODO *** considering implementing as non-case class so that head can be call-by-name and match LazyList impl: https://github.com/scala/scala/blob/6b4d32c3f518d21a798e8d3cf4a8c35866afa8e2/src/library/scala/collection/immutable/LazyList.scala#L1096 final case class NonEmptyLazyList[+A](head: A, tail: LazyList[A]) { - /** * Return the head and tail into a single list * {{{ @@ -53,8 +53,8 @@ final case class NonEmptyLazyList[+A](head: A, tail: LazyList[A]) { * }}} */ def init: LazyList[A] = tail match { - case Nil => LazyList.empty - case t => head #:: t.init + case LazyList() => LazyList.empty + case t => head #:: t.init } /** @@ -228,13 +228,13 @@ final case class NonEmptyLazyList[+A](head: A, tail: LazyList[A]) { def traverse[G[_], B](f: A => G[B])(implicit G: Applicative[G]): G[NonEmptyLazyList[B]] = G.map2Eval(f(head), Always(Traverse[List].traverse(tail)(f)))(NonEmptyLazyList(_, _)).value - // TODO *** deal with ListBuffer + // TODO *** Builder def coflatMap[B](f: NonEmptyLazyList[A] => B): NonEmptyLazyList[B] = { val buf = ListBuffer.empty[B] @tailrec def consume(as: List[A]): List[B] = as match { case Nil => buf.toList - case a #:: as => + case a :: as => buf += f(NonEmptyLazyList(a, as)) consume(as) } @@ -252,7 +252,7 @@ final case class NonEmptyLazyList[+A](head: A, tail: LazyList[A]) { /** * Remove duplicates. Duplicates are checked using `Order[_]` instance. */ - //TODO *** deal with ListBuffer + //TODO *** Builder def distinct[AA >: A](implicit O: Order[AA]): NonEmptyLazyList[AA] = { implicit val ord = O.toOrdering @@ -281,10 +281,10 @@ final case class NonEmptyLazyList[+A](head: A, tail: LazyList[A]) { @tailrec def go(h: A, rest: LazyList[A], acc: LazyList[A]): NonEmptyLazyList[A] = rest match { - case Nil => NonEmptyLazyList(h, acc) - case h1 #:: t1 => go(h1, t1, h #:: acc) + case LazyList() => NonEmptyLazyList(h, acc) + case h1 #:: t1 => go(h1, t1, h #:: acc) } - go(head, tail, Nil) + go(head, tail, LazyList()) } /** @@ -299,15 +299,14 @@ final case class NonEmptyLazyList[+A](head: A, tail: LazyList[A]) { * }}} */ def zipWith[B, C](b: NonEmptyLazyList[B])(f: (A, B) => C): NonEmptyLazyList[C] = { - // TODO *** go back and find all the Nils and make them empty @tailrec def zwRev(as: LazyList[A], bs: LazyList[B], acc: LazyList[C]): LazyList[C] = (as, bs) match { - case (Nil, _) => acc - case (_, Nil) => acc - case (x #:: xs, y #:: ys) => zwRev(xs, ys, f(x, y) #:: acc) + case (LazyList(), _) => acc + case (_, LazyList()) => acc + case (x #:: xs, y #:: ys) => zwRev(xs, ys, f(x, y) #:: acc) } - NonEmptyLazyList(f(head, b.head), zwRev(tail, b.tail, Nil).reverse) + NonEmptyLazyList(f(head, b.head), zwRev(tail, b.tail, LazyList()).reverse) } /** @@ -319,7 +318,7 @@ final case class NonEmptyLazyList[+A](head: A, tail: LazyList[A]) { * scala> nell.zipWithIndex * res0: cats.data.NonEmptyLazyList[(String, Int)] = NonEmptyLazyList((a,0), (b,1), (c,2)) * }}} - */ // TODO *** List builder + */ // TODO *** Builder def zipWithIndex: NonEmptyLazyList[(A, Int)] = { val bldr = List.newBuilder[(A, Int)] var idx = 1 @@ -373,7 +372,7 @@ final case class NonEmptyLazyList[+A](head: A, tail: LazyList[A]) { * scala> nell.groupBy(_ >= 0) * res0: SortedMap[Boolean, cats.data.NonEmptyLazyList[Int]] = Map(false -> NonEmptyLazyList(-2, -5), true -> NonEmptyLazyList(12, 3)) * }}} - */ // TODO builder + */ // TODO *** Builder def groupBy[B](f: A => B)(implicit B: Order[B]): SortedMap[B, NonEmptyLazyList[A]] = { implicit val ordering: Ordering[B] = B.toOrdering var m = TreeMap.empty[B, mutable.Builder[A, LazyList[A]]] @@ -417,7 +416,7 @@ final case class NonEmptyLazyList[+A](head: A, tail: LazyList[A]) { * res0: NonEmptyMap[Int,String] = Map(0 -> c, 1 -> b, 2 -> d) *}}} * - */ // TODO *** not lazy. probably ok though + */ def toNem[T, U](implicit ev: A <:< (T, U), order: Order[T]): NonEmptyMap[T, U] = NonEmptyMap.fromMapUnsafe(SortedMap(toLazyList.map(ev): _*)(order.toOrdering)) @@ -441,11 +440,11 @@ object NonEmptyLazyList extends NonEmptyLazyListInstances { def ofInitLast[A](init: LazyList[A], last: A): NonEmptyLazyList[A] = init match { - case Nil => NonEmptyLazyList(last, Nil) - case h #:: t => NonEmptyLazyList(h, t :+ last) + case LazyList() => NonEmptyLazyList(last, LazyList()) + case h #:: t => NonEmptyLazyList(h, t :+ last) } - def one[A](head: A): NonEmptyLazyList[A] = NonEmptyLazyList(head, Nil) + def one[A](head: A): NonEmptyLazyList[A] = NonEmptyLazyList(head, LazyList()) /** * Create a `NonEmptyLazyList` from a `LazyList`. @@ -458,8 +457,8 @@ object NonEmptyLazyList extends NonEmptyLazyListInstances { */ def fromLazyList[A](l: LazyList[A]): Option[NonEmptyLazyList[A]] = l match { - case Nil => None - case h #:: t => Some(NonEmptyLazyList(h, t)) + case LazyList() => None + case h #:: t => Some(NonEmptyLazyList(h, t)) } /** @@ -471,13 +470,13 @@ object NonEmptyLazyList extends NonEmptyLazyListInstances { */ def fromLazyListUnsafe[A](l: LazyList[A]): NonEmptyLazyList[A] = l match { - case Nil => throw new IllegalArgumentException("Cannot create NonEmptyLazyList from empty LazyList") - case h #:: t => NonEmptyLazyList(h, t) + case LazyList() => throw new IllegalArgumentException("Cannot create NonEmptyLazyList from empty LazyList") + case h #:: t => NonEmptyLazyList(h, t) } - // TODO *** toList + // TODO *** will Foldable have a toLazyList??? def fromFoldable[F[_], A](fa: F[A])(implicit F: Foldable[F]): Option[NonEmptyLazyList[A]] = - fromLazyList(F.toList(fa)) + fromLazyList(F.toLazyList(fa)) def fromReducible[F[_], A](fa: F[A])(implicit F: Reducible[F]): NonEmptyLazyList[A] = F.toNonEmptyLazyList(fa) @@ -541,11 +540,11 @@ sealed abstract private[data] class NonEmptyLazyListInstances extends NonEmptyLa def nonEmptyTraverse[G[_], A, B](nell: NonEmptyLazyList[A])(f: A => G[B])(implicit G: Apply[G]): G[NonEmptyLazyList[B]] = Foldable[List] - .reduceRightToOption[A, G[List[B]]](nell.tail)(a => G.map(f(a))(_ #:: Nil)) { (a, lglb) => + .reduceRightToOption[A, G[List[B]]](nell.tail)(a => G.map(f(a))(_ #:: LazyList())) { (a, lglb) => G.map2Eval(f(a), lglb)(_ #:: _) } .map { - case None => G.map(f(nell.head))(NonEmptyLazyList(_, Nil)) + case None => G.map(f(nell.head))(NonEmptyLazyList(_, LazyList())) case Some(gtail) => G.map2(f(nell.head), gtail)(NonEmptyLazyList(_, _)) } .value @@ -562,7 +561,7 @@ sealed abstract private[data] class NonEmptyLazyListInstances extends NonEmptyLa override def foldMap[A, B](fa: NonEmptyLazyList[A])(f: A => B)(implicit B: Monoid[B]): B = B.combineAll(fa.toLazyList.iterator.map(f)) - // TODO *** list buffer + // TODO *** Builder def tailRecM[A, B](a: A)(f: A => NonEmptyLazyList[Either[A, B]]): NonEmptyLazyList[B] = { val buf = new ListBuffer[B] @tailrec def go(v: NonEmptyLazyList[Either[A, B]]): Unit = v.head match { @@ -608,7 +607,6 @@ sealed abstract private[data] class NonEmptyLazyListInstances extends NonEmptyLa override def exists[A](fa: NonEmptyLazyList[A])(p: A => Boolean): Boolean = fa.exists(p) - // TODO *** not sure what to do about this override def toList[A](fa: NonEmptyLazyList[A]): List[A] = fa.toLazyList.toList override def toNonEmptyLazyList[A](fa: NonEmptyLazyList[A]): NonEmptyLazyList[A] = fa From a666b39b62e0165fda0f9096cf9c352430d805e9 Mon Sep 17 00:00:00 2001 From: Nathaniel May Date: Thu, 27 Jun 2019 12:23:16 -0400 Subject: [PATCH 05/12] added TODOs, replaced calls to NonEmptyStream --- .../test/scala/cats/tests/OneAndSuite.scala | 129 +++++++++--------- 1 file changed, 64 insertions(+), 65 deletions(-) diff --git a/tests/src/test/scala/cats/tests/OneAndSuite.scala b/tests/src/test/scala/cats/tests/OneAndSuite.scala index 4f43391c92..f409719514 100644 --- a/tests/src/test/scala/cats/tests/OneAndSuite.scala +++ b/tests/src/test/scala/cats/tests/OneAndSuite.scala @@ -1,8 +1,8 @@ import cats.kernel.laws.discipline.{EqTests, SemigroupTests} -import cats.instances.stream._ -import cats.data.{NonEmptyStream, OneAnd} +import cats.data.OneAnd +import cats.`scala-2.13+`.data.NonEmptyLazyList // TODO how to import / does it even belong there? import cats.laws.discipline.{ ApplicativeTests, ComonadTests, @@ -16,9 +16,8 @@ import cats.laws.discipline.{ SerializableTests, TraverseTests } -import cats.laws.discipline.arbitrary._ -import kernel.compat.scalaVersionSpecific._ -import compat.lazyList.toLazyList + +// TODO not sure how to convert some of these `OneAnd` specific-tests class OneAndSuite extends CatsSuite { // Lots of collections here.. telling ScalaCheck to calm down a bit implicit override val generatorDrivenConfig: PropertyCheckConfiguration = @@ -37,8 +36,8 @@ class OneAndSuite extends CatsSuite { checkAll("Traverse[OneAnd[ListWrapper, A]]", SerializableTests.serializable(Traverse[OneAnd[ListWrapper, ?]])) } - checkAll("OneAnd[Stream, Int]", ReducibleTests[OneAnd[LazyList, ?]].reducible[Option, Int, Int]) - checkAll("Reducible[OneAnd[Stream, ?]]", SerializableTests.serializable(Reducible[OneAnd[LazyList, ?]])) + checkAll("OneAnd[LazyList, Int]", ReducibleTests[OneAnd[LazyList, ?]].reducible[Option, Int, Int]) + checkAll("Reducible[OneAnd[LazyList, ?]]", SerializableTests.serializable(Reducible[OneAnd[LazyList, ?]])) implicit val iso = SemigroupalTests.Isomorphisms .invariant[OneAnd[ListWrapper, ?]](OneAnd.catsDataFunctorForOneAnd(ListWrapper.functor)) @@ -66,9 +65,9 @@ class OneAndSuite extends CatsSuite { { implicit val alternative = ListWrapper.alternative checkAll("OneAnd[ListWrapper, Int]", SemigroupKTests[OneAnd[ListWrapper, ?]].semigroupK[Int]) - checkAll("OneAnd[Stream, Int]", SemigroupTests[OneAnd[LazyList, Int]].semigroup) + checkAll("OneAnd[LazyList, Int]", SemigroupTests[OneAnd[LazyList, Int]].semigroup) checkAll("SemigroupK[OneAnd[ListWrapper, A]]", SerializableTests.serializable(SemigroupK[OneAnd[ListWrapper, ?]])) - checkAll("Semigroup[NonEmptyStream[Int]]", SerializableTests.serializable(Semigroup[OneAnd[LazyList, Int]])) + checkAll("Semigroup[NonEmptyLazyList[Int]]", SerializableTests.serializable(Semigroup[OneAnd[LazyList, Int]])) } { @@ -79,19 +78,19 @@ class OneAndSuite extends CatsSuite { { // Test functor and subclasses don't have implicit conflicts - implicitly[Functor[NonEmptyStream]] - implicitly[Monad[NonEmptyStream]] - implicitly[Comonad[NonEmptyStream]] + implicitly[Functor[NonEmptyLazyList]] + implicitly[Monad[NonEmptyLazyList]] + implicitly[Comonad[NonEmptyLazyList]] } implicit val iso2 = SemigroupalTests.Isomorphisms.invariant[OneAnd[LazyList, ?]] //OneAnd's tailRecM fails on LazyList due to the fact that. todo: replace NonEmptyStream with NonEmptyLazyList using newtype https://github.com/typelevel/cats/issues/2903 - checkAll("NonEmptyStream[Int]", MonadTests[NonEmptyStream].stackUnsafeMonad[Int, Int, Int]) - checkAll("Monad[NonEmptyStream[A]]", SerializableTests.serializable(Monad[NonEmptyStream])) + checkAll("NonEmptyLazyList[Int]", MonadTests[NonEmptyLazyList].stackUnsafeMonad[Int, Int, Int]) + checkAll("Monad[NonEmptyLazyList[A]]", SerializableTests.serializable(Monad[NonEmptyLazyStream])) - checkAll("NonEmptyStream[Int]", ComonadTests[NonEmptyStream].comonad[Int, Int, Int]) - checkAll("Comonad[NonEmptyStream[A]]", SerializableTests.serializable(Comonad[NonEmptyStream])) + checkAll("NonEmptyLazyList[Int]", ComonadTests[NonEmptyLazyList].comonad[Int, Int, Int]) + checkAll("Comonad[NonEmptyLazyList[A]]", SerializableTests.serializable(Comonad[NonEmptyLazyList])) test("size is consistent with toList.size") { forAll { (oa: OneAnd[Vector, Int]) => @@ -100,61 +99,61 @@ class OneAndSuite extends CatsSuite { } test("Show is not empty and is formatted as expected") { - forAll { (nel: NonEmptyStream[Int]) => + forAll { (nel: NonEmptyLazyList[Int]) => nel.show.nonEmpty should ===(true) - nel.show.startsWith("OneAnd(") should ===(true) - nel.show should ===(implicitly[Show[NonEmptyStream[Int]]].show(nel)) + nel.show.startsWith("NonEmptyLazyList(") should ===(true) + nel.show should ===(implicitly[Show[NonEmptyLazyList[Int]]].show(nel)) nel.show.contains(nel.head.show) should ===(true) } } test("Show is formatted correctly") { - val oneAnd = NonEmptyStream("Test") + val oneAnd = NonEmptyLazyList("Test") oneAnd.show should ===(s"OneAnd(Test, ${compat.lazyList.lazyListString}())") } test("Creating OneAnd + unwrap is identity") { forAll { (i: Int, tail: LazyList[Int]) => val stream = i #:: tail - val oneAnd = NonEmptyStream(i, tail: _*) + val oneAnd = NonEmptyLazyList(i, tail: _*) stream should ===(oneAnd.unwrap) } } - test("NonEmptyStream#find is consistent with Stream#find") { - forAll { (nel: NonEmptyStream[Int], p: Int => Boolean) => - val stream = nel.unwrap - nel.find(p) should ===(stream.find(p)) + test("NonEmptyLazyList#find is consistent with LazyList#find") { + forAll { (nell: NonEmptyLazyList[Int], p: Int => Boolean) => + val lazyList = nell.unwrap + nell.find(p) should ===(lazyList.find(p)) } } - test("NonEmptyStream#exists is consistent with Stream#exists") { - forAll { (nel: NonEmptyStream[Int], p: Int => Boolean) => - val stream = nel.unwrap - nel.exists(p) should ===(stream.exists(p)) + test("NonEmptyLazyList#exists is consistent with LazyList#exists") { + forAll { (nell: NonEmptyLazyList[Int], p: Int => Boolean) => + val lazyList = nell.unwrap + nell.exists(p) should ===(lazyList.exists(p)) } } - test("NonEmptyStream#forall is consistent with Stream#forall") { - forAll { (nel: NonEmptyStream[Int], p: Int => Boolean) => - val stream = nel.unwrap - nel.forall(p) should ===(stream.forall(p)) + test("NonEmptyLazyList#forall is consistent with LazyList#forall") { + forAll { (nell: NonEmptyLazyList[Int], p: Int => Boolean) => + val lazyList = nell.unwrap + nell.forall(p) should ===(lazyList.forall(p)) } } - test("NonEmptyStream#map is consistent with Stream#map") { - forAll { (nel: NonEmptyStream[Int], p: Int => String) => - val stream = nel.unwrap - nel.map(p).unwrap should ===(stream.map(p)) + test("NonEmptyLazy#map is consistent with LazyList#map") { + forAll { (nell: NonEmptyLazyList[Int], p: Int => String) => + val lazyList = nel.unwrap + nell.map(p).unwrap should ===(lazyList.map(p)) } } - test("NonEmptyStream#nonEmptyPartition remains sorted") { - forAll { (nes: NonEmptyStream[Int], f: Int => Either[String, String]) => - val nesf = nes.map(f) - val sortedStream = (nesf.head #:: nesf.tail).sorted - val sortedNes = OneAnd(sortedStream.head, sortedStream.tail) - val ior = Reducible[NonEmptyStream].nonEmptyPartition(sortedNes)(identity) + test("NonEmptyLazyList#nonEmptyPartition remains sorted") { + forAll { (nell: NonEmptyLazyList[Int], f: Int => Either[String, String]) => + val nellf = nes.map(f) + val sortedLazyList = (nellf.head #:: nellf.tail).sorted + val sortedNell = OneAnd(sortedLazyList.head, sortedLazyList.tail) + val ior = Reducible[NonEmptyLazyList].nonEmptyPartition(sortedNell)(identity) ior.left.map(xs => xs.sorted should ===(xs)) ior.right.map(xs => xs.sorted should ===(xs)) @@ -162,45 +161,45 @@ class OneAndSuite extends CatsSuite { } test("reduceLeft consistent with foldLeft") { - forAll { (nel: NonEmptyStream[Int], f: (Int, Int) => Int) => - nel.reduceLeft(f) should ===(nel.tail.foldLeft(nel.head)(f)) + forAll { (nell: NonEmptyLazyList[Int], f: (Int, Int) => Int) => + nell.reduceLeft(f) should ===(nell.tail.foldLeft(nell.head)(f)) } } test("reduceRight consistent with foldRight") { - forAll { (nel: NonEmptyStream[Int], f: (Int, Eval[Int]) => Eval[Int]) => - val got = nel.reduceRight(f).value - val last :: rev = nel.unwrap.toList.reverse + forAll { (nell: NonEmptyLazyList[Int], f: (Int, Eval[Int]) => Eval[Int]) => + val got = nell.reduceRight(f).value + val last :: rev = nell.unwrap.toList.reverse val expected = rev.reverse.foldRight(last)((a, b) => f(a, Now(b)).value) got should ===(expected) } } test("reduce consistent with fold") { - forAll { (nel: NonEmptyStream[Int]) => - nel.reduce should ===(nel.fold) + forAll { (nell: NonEmptyLazyList[Int]) => + nell.reduce should ===(nel.fold) } } test("reduce consistent with reduceK") { - forAll { (nel: NonEmptyStream[Option[Int]]) => - nel.reduce(SemigroupK[Option].algebra[Int]) should ===(nel.reduceK) + forAll { (nell: NonEmptyLazyList[Option[Int]]) => + nell.reduce(SemigroupK[Option].algebra[Int]) should ===(nel.reduceK) } } test("reduceLeftToOption consistent with foldLeft + Option") { - forAll { (nel: NonEmptyStream[Int], f: Int => String, g: (String, Int) => String) => - val expected = nel.tail.foldLeft(Option(f(nel.head))) { (opt, i) => + forAll { (nell: NonEmptyLazyList[Int], f: Int => String, g: (String, Int) => String) => + val expected = nell.tail.foldLeft(Option(f(nel.head))) { (opt, i) => opt.map(s => g(s, i)) } - nel.reduceLeftToOption(f)(g) should ===(expected) + nell.reduceLeftToOption(f)(g) should ===(expected) } } test("reduceRightToOption consistent with foldRight + Option") { - forAll { (nel: NonEmptyStream[Int], f: Int => String, g: (Int, Eval[String]) => Eval[String]) => - val got = nel.reduceRightToOption(f)(g).value - val last :: rev = nel.unwrap.toList.reverse + forAll { (nell: NonEmptyLazyList[Int], f: Int => String, g: (Int, Eval[String]) => Eval[String]) => + val got = nell.reduceRightToOption(f)(g).value + val last :: rev = nell.unwrap.toList.reverse val expected = rev.reverse.foldRight(Option(f(last))) { (i, opt) => opt.map(s => g(i, Now(s)).value) } @@ -209,21 +208,21 @@ class OneAndSuite extends CatsSuite { } test("filter includes elements based on a predicate") { - forAll { (nes: NonEmptyStream[Int], pred: Int => Boolean) => - nes.filter(pred) should ===(nes.unwrap.filter(pred)) + forAll { (nell: NonEmptyLazyList[Int], pred: Int => Boolean) => + nell.filter(pred) should ===(nell.unwrap.filter(pred)) } } } -class ReducibleNonEmptyStreamSuite extends ReducibleSuite[NonEmptyStream]("NonEmptyStream") { - def iterator[T](nes: NonEmptyStream[T]): Iterator[T] = - (nes.head #:: nes.tail).iterator +class ReducibleNonEmptyLazyListSuite extends ReducibleSuite[NonEmptyLazyList]("NonEmptyLazyList") { + def iterator[T](nell: NonEmptyLazyList[T]): Iterator[T] = + (nell.head #:: nell.tail).iterator - def range(start: Long, endInclusive: Long): NonEmptyStream[Long] = { + def range(start: Long, endInclusive: Long): NonEmptyLazyList[Long] = { // if we inline this we get a bewildering implicit numeric widening // error message in Scala 2.10 val tailStart: Long = start + 1L - NonEmptyStream(start, toLazyList(tailStart.to(endInclusive))) + NonEmptyLazyList(start, toLazyList(tailStart.to(endInclusive))) } } From d6efd346fc3d00381fc05bc13bb68ca05f4e9813 Mon Sep 17 00:00:00 2001 From: Nathaniel May Date: Thu, 27 Jun 2019 12:34:26 -0400 Subject: [PATCH 06/12] prePR changes --- tests/src/test/scala/cats/tests/OneAndSuite.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/src/test/scala/cats/tests/OneAndSuite.scala b/tests/src/test/scala/cats/tests/OneAndSuite.scala index f409719514..c12cbaeb75 100644 --- a/tests/src/test/scala/cats/tests/OneAndSuite.scala +++ b/tests/src/test/scala/cats/tests/OneAndSuite.scala @@ -1,4 +1,3 @@ - import cats.kernel.laws.discipline.{EqTests, SemigroupTests} import cats.data.OneAnd From acacf25af7ac56098e34bfa9e2ca3ccd25b3e601 Mon Sep 17 00:00:00 2001 From: Nathaniel May Date: Fri, 28 Jun 2019 12:12:07 -0400 Subject: [PATCH 07/12] first pass at intended impl --- .../cats/data/NonEmptyLazyList.scala | 818 +++++++----------- 1 file changed, 296 insertions(+), 522 deletions(-) diff --git a/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala index 55ba132ed7..785c03d3ae 100644 --- a/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala +++ b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala @@ -1,510 +1,340 @@ package cats.data -import cats.data.NonEmptyLazyList.ZipNonEmptyLazyList -import cats.instances.lazyList._ -import cats.syntax.order._ +import NonEmptyLazyListImpl.{NonEmptyLazyList, create, unwrap} +import cats.{Apply, Bimonad, Eval, Foldable, Monad, NonEmptyReducible, NonEmptyTraverse, Order, Reducible, SemigroupK, Show} +import cats.kernel.{Order, _} import scala.annotation.tailrec -import scala.collection.immutable.{SortedMap, TreeMap, TreeSet} -import scala.collection.mutable +import scala.collection.immutable._ import scala.collection.mutable.ListBuffer -// TODO *** will handle mutable builders later - input appreciated -// TODO *** 2.13 compiler reports matching empty cases on LazyList() - not Empty because of new `State` impl. Better way? -/** - * A data type which represents a non empty lazy list of A, with - * single element (head) and optional structure (tail). - */ -// TODO *** considering implementing as non-case class so that head can be call-by-name and match LazyList impl: https://github.com/scala/scala/blob/6b4d32c3f518d21a798e8d3cf4a8c35866afa8e2/src/library/scala/collection/immutable/LazyList.scala#L1096 -final case class NonEmptyLazyList[+A](head: A, tail: LazyList[A]) { - /** - * Return the head and tail into a single list - * {{{ - * scala> import cats.data.NonEmptyLazyList - * scala> val nell = NonEmptyLazyList.of(1, 2, 3, 4, 5) - * scala> nell.toLazyList - * res0: scala.collection.immutable.LazyList[Int] = LazyList(1, 2, 3, 4, 5) - * }}} - */ - def toLazyList: LazyList[A] = head #:: tail - - /** - * Selects the last element - * {{{ - * scala> import cats.data.NonEmptyLazyList - * scala> val nell = NonEmptyLazyList.of(1, 2, 3, 4, 5) - * scala> nell.last - * res0: Int = 5 - * }}} - */ - def last: A = tail.lastOption match { - case None => head - case Some(a) => a - } +private[data] object NonEmptyLazyListImpl extends NonEmptyLazyListInstances { + // TODO belongs in package object: + type NonEmptyLazyList[+A] = NonEmptyLazyListImpl.Type[A] + //val NonEmptyLazyList = NonEmptyChainImpl + + private[data] type Base + private[data] trait Tag extends Any + type Type[+A] <: Base with Tag + + private[cats] def create[A](s: LazyList[A]): Type[A] = + s.asInstanceOf[Type[A]] + + private[cats] def unwrap[A](s: Type[A]): LazyList[A] = + s.asInstanceOf[LazyList[A]] + + def fromLazyList[A](as: LazyList[A]): Option[NonEmptyLazyList[A]] = + if (as.nonEmpty) Option(create(as)) else None + + def fromLazyListUnsafe[A](ll: LazyList[A]): NonEmptyLazyList[A] = + if (ll.nonEmpty) create(ll) + else throw new IllegalArgumentException("Cannot create NonEmptyLazyList from empty LazyList") + + def fromNonEmptyList[A](as: NonEmptyList[A]): NonEmptyLazyList[A] = + create(LazyList.from(as.toList)) + + def fromNonEmptyVector[A](as: NonEmptyVector[A]): NonEmptyLazyList[A] = + create(LazyList.from(as.toVector)) + + def fromSeq[A](as: Seq[A]): Option[NonEmptyLazyList[A]] = + if (as.nonEmpty) Option(create(LazyList.from(as))) else None + + def fromLazyListPrepend[A](a: A, ca: LazyList[A]): NonEmptyLazyList[A] = + create(a +: ca) + + def fromLazyListAppend[A](ca: LazyList[A], a: A): NonEmptyLazyList[A] = + create(ca :+ a) + + def apply[A](a: => A, as: A*): NonEmptyLazyList[A] = + create(LazyList.concat(LazyList(a), LazyList.from(as))) + + implicit def catsNonEmptyLazyListOps[A](value: NonEmptyLazyList[A]): NonEmptyLazyListOps[A] = + new NonEmptyLazyListOps(value) +} + +class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A]) extends AnyVal { /** - * Selects all elements except the last - * - * {{{ - * scala> import cats.data.NonEmptyLazyList - * scala> val nell = NonEmptyLazyList.of(1, 2, 3, 4, 5) - * scala> nell.init - * res0: scala.collection.immutable.LazyList[Int] = LazyList(1, 2, 3, 4) - * }}} + * Converts this NonEmptyLazyList to a `LazyList` */ - def init: LazyList[A] = tail match { - case LazyList() => LazyList.empty - case t => head #:: t.init - } + final def toLazyList: LazyList[A] = NonEmptyLazyListImpl.unwrap(value) /** - * The size of this NonEmptyLazyList - * - * {{{ - * scala> import cats.data.NonEmptyLazyList - * scala> val nell = NonEmptyLazyList.of(1, 2, 3, 4, 5) - * scala> nell.size - * res0: Int = 5 - * }}} + * Returns the last element */ - def size: Int = 1 + tail.size - - def length: Int = size + final def last: A = toLazyList.last /** - * Applies f to all the elements of the structure + * Returns all elements but the last */ - def map[B](f: A => B): NonEmptyLazyList[B] = - NonEmptyLazyList(f(head), tail.map(f)) + final def init: LazyList[A] = toLazyList.init - def ++[AA >: A](l: LazyList[AA]): NonEmptyLazyList[AA] = - concat(l) - - def concat[AA >: A](other: LazyList[AA]): NonEmptyLazyList[AA] = - NonEmptyLazyList(head, tail #::: other) + /** + * Returns the size of this NonEmptyLazyList + */ + final def size: Int = toLazyList.size /** - * Append another NonEmptyLazyList + * Returns the length of this NonEmptyLazyList */ - def concatNel[AA >: A](other: NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = - NonEmptyLazyList(head, tail #::: other.toLazyList) + final def length: Int = size - def flatMap[B](f: A => NonEmptyLazyList[B]): NonEmptyLazyList[B] = - f(head) ++ tail.flatMap(f.andThen(_.toLazyList)) + /** + * Returns a new NonEmptyLazyList consisting of `a` followed by this + */ + final def prepend[AA >: A](a: AA): NonEmptyLazyList[AA] = + create(a #:: toLazyList) - def #::[AA >: A](a: AA): NonEmptyLazyList[AA] = + /** + * Alias for [[prepend]]. + */ + final def +:[AA >: A](a: AA): NonEmptyLazyList[AA] = prepend(a) - def prepend[AA >: A](a: AA): NonEmptyLazyList[AA] = - NonEmptyLazyList(a, head #:: tail) - /** - * Alias for append - * - * {{{ - * scala> import cats.data.NonEmptyLazyList - * scala> val nell = NonEmptyLazyList.of(1, 2, 3) - * scala> nell :+ 4 - * res0: cats.data.NonEmptyLazyList[Int] = NonEmptyLazyList(1, 2, 3, 4) - * }}} + * Alias for [[prepend]]. */ - def :+[AA >: A](a: AA): NonEmptyLazyList[AA] = - append(a) - - def append[AA >: A](a: AA): NonEmptyLazyList[AA] = - NonEmptyLazyList(head, tail :+ a) + final def #::[AA >: A](a: AA): NonEmptyLazyList[AA] = + prepend(a) /** - * Alias for concatNell - * - * {{{ - * scala> import cats.data.NonEmptyLazyList - * scala> val nell = NonEmptyLazyList.of(1, 2, 3) - * scala> nell #::: NonEmptyLazyList.of(4, 5) - * res0: cats.data.NonEmptyLazyList[Int] = NonEmptyLazyList(1, 2, 3, 4, 5) - * }}} - */ - def #:::[AA >: A](other: NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = - other.concatNel(this) - - /** - * Remove elements not matching the predicate - * - * {{{ - * scala> import cats.data.NonEmptyLazyList - * scala> val nell = NonEmptyLazyList.of(1, 2, 3, 4, 5) - * scala> nell.filter(_ < 3) - * res0: scala.collection.immutable.LazyList[Int] = LazyList(1, 2) - * }}} - */ - def filter(p: A => Boolean): LazyList[A] = { - val ftail = tail.filter(p) - if (p(head)) head #:: ftail - else ftail - } + * Returns a new NonEmptyLazyList consisting of this followed by `a` + */ + final def append[AA >: A](a: AA): NonEmptyLazyList[AA] = + create(toLazyList :+ a) /** - * Remove elements matching the predicate - * - * {{{ - * scala> import cats.data.NonEmptyLazyList - * scala> val nell = NonEmptyLazyList.of(1, 2, 3, 4, 5) - * scala> nell.filterNot(_ < 3) - * res0: scala.collection.immutable.LazyList[Int] = LazyList(3, 4, 5) - * }}} + * Alias for [[append]]. */ - def filterNot(p: A => Boolean): LazyList[A] = { - val ftail = tail.filterNot(p) - if (p(head)) ftail - else head #:: ftail - } + final def :+[AA >: A](a: AA): NonEmptyLazyList[AA] = + append(a) /** - * Builds a new `LazyList` by applying a partial function to - * all the elements from this `NonEmptyLazyList` on which the function is defined - * - * {{{ - * scala> import cats.data.NonEmptyLazyList - * scala> val nell = NonEmptyLazyList.of(1, 2, 3, 4, 5) - * scala> nell.collect { case v if v < 3 => v } - * res0: scala.collection.immutable.LazyList[Int] = LazyList(1, 2) - * scala> nell.collect { - * | case v if v % 2 == 0 => "even" - * | case _ => "odd" - * | } - * res1: scala.collection.immutable.LazyList[String] = LazyList(odd, even, odd, even, odd) - * }}} - */ - def collect[B](pf: PartialFunction[A, B]): LazyList[B] = - if (pf.isDefinedAt(head)) { - pf.apply(head) #:: tail.collect(pf) - } else { - tail.collect(pf) - } + * concatenates this with `ll` + */ + final def concat[AA >: A](ll: LazyList[AA]): NonEmptyLazyList[AA] = + create(toLazyList ++ ll) /** - * Find the first element matching the predicate, if one exists + * Concatenates this with `nell` */ - def find(p: A => Boolean): Option[A] = - if (p(head)) Some(head) - else tail.find(p) + final def concatNell[AA >: A](nell: NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = + create(toLazyList ++ nell.toLazyList) /** - * Check whether at least one element satisfies the predicate + * Alias for concatNell */ - def exists(p: A => Boolean): Boolean = - p(head) || tail.exists(p) + final def ++[AA >: A](nell: NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = + concatNell(nell) /** - * Check whether all elements satisfy the predicate + * Appends the given LazyList */ - def forall(p: A => Boolean): Boolean = - p(head) && tail.forall(p) + final def appendLazyList[AA >: A](nell: LazyList[AA]): NonEmptyLazyList[AA] = + if (nell.isEmpty) value + else create(toLazyList ++ nell) /** - * Left-associative fold on the structure using f. + * Alias for `appendLazyList` */ - def foldLeft[B](b: B)(f: (B, A) => B): B = - tail.foldLeft(f(b, head))(f) + final def :++[AA >: A](c: LazyList[AA]): NonEmptyLazyList[AA] = + appendLazyList(c) /** - * Right-associative fold on the structure using f. + * Prepends the given LazyList */ - def foldRight[B](lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = - Foldable[LazyList].foldRight(toLazyList, lb)(f) + final def prependLazyList[AA >: A](c: LazyList[AA]): NonEmptyLazyList[AA] = + if (c.isEmpty) value + else create(c ++ toLazyList) /** - * Left-associative reduce using f. + * Prepends the given NonEmptyLazyList */ - def reduceLeft[AA >: A](f: (AA, AA) => AA): AA = - tail.foldLeft[AA](head)(f) + final def prependNell[AA >: A](c: NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = + create(c.toLazyList ++ toLazyList) /** - * Reduce using the `Semigroup` of `AA`. + * Alias for `prependNell` */ - def reduce[AA >: A](implicit S: Semigroup[AA]): AA = - S.combineAllOption(toLazyList).get + final def ++:[AA >: A](c: NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = + prependNell(c) - def traverse[G[_], B](f: A => G[B])(implicit G: Applicative[G]): G[NonEmptyLazyList[B]] = - G.map2Eval(f(head), Always(Traverse[List].traverse(tail)(f)))(NonEmptyLazyList(_, _)).value + /** + * Converts this NonEmptyLazyList to a `NonEmptyList`. + */ // TODO also add toNonEmptyLazyList to NonEmptyList? + final def toNonEmptyList: NonEmptyList[A] = + NonEmptyList.fromListUnsafe(toLazyList.toList) - // TODO *** Builder - def coflatMap[B](f: NonEmptyLazyList[A] => B): NonEmptyLazyList[B] = { - val buf = ListBuffer.empty[B] - @tailrec def consume(as: List[A]): List[B] = - as match { - case Nil => buf.toList - case a :: as => - buf += f(NonEmptyLazyList(a, as)) - consume(as) - } - NonEmptyLazyList(f(this), consume(tail)) - } + /** + * Converts this LazyList to a `NonEmptyVector`. + */ + final def toNonEmptyVector: NonEmptyVector[A] = + NonEmptyVector.fromVectorUnsafe(toLazyList.toVector) - def ===[AA >: A](o: NonEmptyLazyList[AA])(implicit AA: Eq[AA]): Boolean = - ((this.head: AA) === o.head) && (this.tail: LazyList[AA]) === o.tail + /** + * Returns the head and tail of this NonEmptyLazyList + */ + final def uncons: (A, LazyList[A]) = + (head, tail) - def show[AA >: A](implicit AA: Show[AA]): String = - toLazyList.iterator.map(AA.show).mkString("NonEmptyLazyList(", ", ", ")") + /** + * Returns the first element + */ + final def head: A = uncons._1 - override def toString: String = s"NonEmpty$toLazyList" + /** + * Returns all but the first element + */ + final def tail: LazyList[A] = uncons._2 /** - * Remove duplicates. Duplicates are checked using `Order[_]` instance. + * Tests if some element is contained in this NonEmptyLazyList */ - //TODO *** Builder - def distinct[AA >: A](implicit O: Order[AA]): NonEmptyLazyList[AA] = { - implicit val ord = O.toOrdering + final def contains(a: A)(implicit A: Eq[A]): Boolean = + toLazyList.contains(a) - val buf = ListBuffer.empty[AA] - tail.foldLeft(TreeSet(head: AA)) { (elementsSoFar, b) => - if (elementsSoFar(b)) elementsSoFar - else { - buf += b; elementsSoFar + b - } - } + /** + * Tests whether a predicate holds for all elements + */ + final def forall(p: A => Boolean): Boolean = + toLazyList.forall(p) - NonEmptyLazyList(head, buf.toList) - } + /** + * Tests whether a predicate holds for at least one element of this LazyList + */ + final def exists(f: A => Boolean): Boolean = + toLazyList.exists(f) /** - * Reverse this `NonEmptyLazyList`. - * - * {{{ - * scala> import cats.data.NonEmptyLazyList - * scala> val nell = NonEmptyLazyList.of(1, 2, 3) - * scala> nell.reverse - * res0: cats.data.NonEmptyLazyList[Int] = NonEmptyLazyList(3, 2, 1) - * }}} - */ - def reverse: NonEmptyLazyList[A] = { - @tailrec - def go(h: A, rest: LazyList[A], acc: LazyList[A]): NonEmptyLazyList[A] = - rest match { - case LazyList() => NonEmptyLazyList(h, acc) - case h1 #:: t1 => go(h1, t1, h #:: acc) - } - go(head, tail, LazyList()) - } + * Returns the first value that matches the given predicate. + */ + final def find(f: A => Boolean): Option[A] = + toLazyList.find(f) /** - * Zips this `NonEmptyLazyList` with another `NonEmptyLazyList` and applies a function for each pair of elements. - * - * {{{ - * scala> import cats.data.NonEmptyLazyList - * scala> val as = NonEmptyLazyList.of(1, 2, 3) - * scala> val bs = NonEmptyLazyList.of("A", "B", "C") - * scala> as.zipWith(bs)(_ + _) - * res0: cats.data.NonEmptyLazyList[String] = NonEmptyLazyList(1A, 2B, 3C) - * }}} - */ - def zipWith[B, C](b: NonEmptyLazyList[B])(f: (A, B) => C): NonEmptyLazyList[C] = { - @tailrec - def zwRev(as: LazyList[A], bs: LazyList[B], acc: LazyList[C]): LazyList[C] = (as, bs) match { - case (LazyList(), _) => acc - case (_, LazyList()) => acc - case (x #:: xs, y #:: ys) => zwRev(xs, ys, f(x, y) #:: acc) - } + * Returns a new `LazyList` containing all elements where the result of `pf` is final defined. + */ + final def collect[B](pf: PartialFunction[A, B]): LazyList[B] = + toLazyList.collect(pf) - NonEmptyLazyList(f(head, b.head), zwRev(tail, b.tail, LazyList()).reverse) - } + /** + * Finds the first element of this `NonEmptyLazyList` for which the given partial + * function is defined, and applies the partial function to it. + */ + final def collectLazyList[B](pf: PartialFunction[A, B]): Option[B] = toLazyList.collectFirst(pf) /** - * Zips each element of this `NonEmptyLazyList` with its index. - * - * {{{ - * scala> import cats.data.NonEmptyLazyList - * scala> val nell = NonEmptyLazyList.of("a", "b", "c") - * scala> nell.zipWithIndex - * res0: cats.data.NonEmptyLazyList[(String, Int)] = NonEmptyLazyList((a,0), (b,1), (c,2)) - * }}} - */ // TODO *** Builder - def zipWithIndex: NonEmptyLazyList[(A, Int)] = { - val bldr = List.newBuilder[(A, Int)] - var idx = 1 - val it = tail.iterator - while (it.hasNext) { - bldr += ((it.next, idx)) - idx += 1 - } - NonEmptyLazyList((head, 0), bldr.result) - } + * Like `collectFirst` from `scala.collection.Traversable` but takes `A => Option[B]` + * instead of `PartialFunction`s. + */ // TODO manually implemented because LazyList doesn't have collectFirstSome + final def collectFirstSome[B](f: A => Option[B]): Option[B] = + collectLazyList(PartialFunction[A, B] { + case a: A if f(a).isDefined => f(a).get + }) /** - * Sorts this `NonEmptyLazyList` according to an `Order` on transformed `B` from `A` - * - * {{{ - * scala> import cats.data.NonEmptyLazyList - * scala> import cats.instances.int._ - * scala> val nell = NonEmptyLazyList.of(('a', 4), ('z', 1), ('e', 22)) - * scala> nell.sortBy(_._2) - * res0: cats.data.NonEmptyLazyList[(Char, Int)] = NonEmptyLazyList((z,1), (a,4), (e,22)) - * }}} + * Filters all elements of this NonEmptyLazyList that do not satisfy the given predicate. */ - def sortBy[B](f: A => B)(implicit B: Order[B]): NonEmptyLazyList[A] = - // safe: sorting a NonEmptyLazyList cannot produce an empty LazyList - NonEmptyLazyList.fromLazyListUnsafe(toLazyList.sortBy(f)(B.toOrdering)) + final def filter(p: A => Boolean): LazyList[A] = toLazyList.filter(p) /** - * Sorts this `NonEmptyLazyList` according to an `Order` - * - * {{{ - * scala> import cats.data.NonEmptyLazyList - * scala> import cats.instances.int._ - * scala> val nell = NonEmptyLazyList.of(12, 4, 3, 9) - * scala> nell.sorted - * res0: cats.data.NonEmptyLazyList[Int] = NonEmptyLazyList(3, 4, 9, 12) - * }}} + * Filters all elements of this NonEmptyLazyList that satisfy the given predicate. */ - def sorted[AA >: A](implicit AA: Order[AA]): NonEmptyLazyList[AA] = - // safe: sorting a NonEmptyLazyList cannot produce an empty LazyList - NonEmptyLazyList.fromLazyListUnsafe(toLazyList.sorted(AA.toOrdering)) + final def filterNot(p: A => Boolean): LazyList[A] = filter(t => !p(t)) /** - * Groups elements inside this `NonEmptyLazyList` according to the `Order` - * of the keys produced by the given mapping function. - * - * {{{ - * scala> import scala.collection.immutable.SortedMap - * scala> import cats.data.NonEmptyLazyList - * scala> import cats.instances.boolean._ - * scala> val nell = NonEmptyLazyList.of(12, -2, 3, -5) - * scala> nell.groupBy(_ >= 0) - * res0: SortedMap[Boolean, cats.data.NonEmptyLazyList[Int]] = Map(false -> NonEmptyLazyList(-2, -5), true -> NonEmptyLazyList(12, 3)) - * }}} - */ // TODO *** Builder - def groupBy[B](f: A => B)(implicit B: Order[B]): SortedMap[B, NonEmptyLazyList[A]] = { - implicit val ordering: Ordering[B] = B.toOrdering - var m = TreeMap.empty[B, mutable.Builder[A, LazyList[A]]] - - for { elem <- toLazyList } { - val k = f(elem) - - m.get(k) match { - case None => m += ((k, List.newBuilder[A] += elem)) - case Some(builder) => builder += elem - } - } + * Left-associative fold using f. + */ + final def foldLeft[B](b: B)(f: (B, A) => B): B = + toLazyList.foldLeft(b)(f) - m.map { - case (k, v) => (k, NonEmptyLazyList.fromListUnsafe(v.result)) - }: TreeMap[B, NonEmptyLazyList[A]] - } + /** + * Right-associative fold using f. + */ + final def foldRight[B](z: B)(f: (A, B) => B): B = + toLazyList.foldRight(z)(f) /** - * Groups elements inside this `NonEmptyLazyList` according to the `Order` - * of the keys produced by the given mapping function. - * - * {{{ - * scala> import cats.data._ - * scala> import cats.instances.boolean._ - * scala> val nell = NonEmptyLazyList.of(12, -2, 3, -5) - * scala> nell.groupByNem(_ >= 0) - * res0: NonEmptyMap[Boolean, NonEmptyLazyList[Int]] = Map(false -> NonEmptyLazyList(-2, -5), true -> NonEmptyLazyList(12, 3)) - * }}} - */ - def groupByNem[B](f: A => B)(implicit B: Order[B]): NonEmptyMap[B, NonEmptyLazyList[A]] = - NonEmptyMap.fromMapUnsafe(groupBy(f)) - - /** - * Creates new `NonEmptyMap`, similarly to LazyList#toMap from scala standard library. - *{{{ - * scala> import cats.data._ - * scala> import cats.instances.int._ - * scala> val nell = NonEmptyLazyList((0, "a"), LazyList((1, "b"),(0, "c"), (2, "d"))) - * scala> nell.toNem - * res0: NonEmptyMap[Int,String] = Map(0 -> c, 1 -> b, 2 -> d) - *}}} - * - */ - def toNem[T, U](implicit ev: A <:< (T, U), order: Order[T]): NonEmptyMap[T, U] = - NonEmptyMap.fromMapUnsafe(SortedMap(toLazyList.map(ev): _*)(order.toOrdering)) - - /** - * Creates new `NonEmptySet`, similarly to LazyList#toSet from scala standard library. - *{{{ - * scala> import cats.data._ - * scala> import cats.instances.int._ - * scala> val nell = NonEmptyLazyList(1, LazyList(2,2,3,4)) - * scala> nell.toNes - * res0: cats.data.NonEmptySet[Int] = TreeSet(1, 2, 3, 4) - *}}} - */ - def toNes[B >: A](implicit order: Order[B]): NonEmptySet[B] = - NonEmptySet.of(head, tail: _*) -} + * Left-associative reduce using f. + */ + final def reduceLeft(f: (A, A) => A): A = + toLazyList.reduceLeft(f) -object NonEmptyLazyList extends NonEmptyLazyListInstances { + /** + * Apply `f` to the "initial element" of this LazyList and lazily combine it + * with every other value using the given function `g`. + */ // TODO not implemented in LazyList + final def reduceLeftTo[B](f: A => B)(g: (B, A) => B): B = + toLazyList.reduceLeftTo(f)(g) - def of[A](head: A, tail: A*): NonEmptyLazyList[A] = NonEmptyLazyList(head, tail.toLazyList) - def ofInitLast[A](init: LazyList[A], last: A): NonEmptyLazyList[A] = - init match { - case LazyList() => NonEmptyLazyList(last, LazyList()) - case h #:: t => NonEmptyLazyList(h, t :+ last) - } + /** + * Right-associative reduce using f. + */ + final def reduceRight[AA >: A](f: (A, AA) => AA): AA = + toLazyList.reduceRight(f) - def one[A](head: A): NonEmptyLazyList[A] = NonEmptyLazyList(head, LazyList()) + /** + * Apply `f` to the "initial element" of this NonEmptyLazyList and + * lazily combine it with every other value using the given function `g`. + */ // TODO not implemented in LazyList + final def reduceRightTo[B](f: A => B)(g: (A, B) => B): B = + toLazyList.reduceRightTo(f)(g) /** - * Create a `NonEmptyLazyList` from a `LazyList`. - * - * The result will be `None` if the input list is empty and `Some` wrapping a - * `NonEmptyLazyList` otherwise. - * - * @see [[fromListUnsafe]] for an unsafe version that throws an exception if - * the input list is empty. + * Reduce using the Semigroup of A */ - def fromLazyList[A](l: LazyList[A]): Option[NonEmptyLazyList[A]] = - l match { - case LazyList() => None - case h #:: t => Some(NonEmptyLazyList(h, t)) - } + final def reduce[AA >: A](implicit S: Semigroup[AA]): AA = + S.combineAllOption(iterator).get /** - * Create a `NonEmptyLazyList` from a `LazyList`, or throw an - * `IllegalArgumentException` if the input list is empty. - * - * @see [[fromLazyList]] for a safe version that returns `None` if the input list - * is empty. + * Applies the supplied function to each element and returns a new NonEmptyLazyList from the concatenated results */ - def fromLazyListUnsafe[A](l: LazyList[A]): NonEmptyLazyList[A] = - l match { - case LazyList() => throw new IllegalArgumentException("Cannot create NonEmptyLazyList from empty LazyList") - case h #:: t => NonEmptyLazyList(h, t) - } - - // TODO *** will Foldable have a toLazyList??? - def fromFoldable[F[_], A](fa: F[A])(implicit F: Foldable[F]): Option[NonEmptyLazyList[A]] = - fromLazyList(F.toLazyList(fa)) + final def flatMap[B](f: A => NonEmptyLazyList[B]): NonEmptyLazyList[B] = + create(toLazyList.flatMap(f.andThen(_.toLazyList))) - def fromReducible[F[_], A](fa: F[A])(implicit F: Reducible[F]): NonEmptyLazyList[A] = - F.toNonEmptyLazyList(fa) + /** + * Returns the number of elements + */ + final def length: Long = toLazyList.size - class ZipNonEmptyLazyList[A](val value: NonEmptyLazyList[A]) extends AnyVal + /** + * Zips this `NonEmptyLazyList` with another `NonEmptyLazyList` and applies a function for each pair of elements + */ // TODO manually implemented + final def zipWith[B, C](b: NonEmptyLazyList[B])(f: (A, B) => C): NonEmptyLazyList[C] = + create(toLazyList.zip(b.toLazyList).map { case (a, b) => f(a, b) }) - object ZipNonEmptyLazyList { + /** + * Zips each element of this `NonEmptyLazyList` with its index + */ + final def zipWithIndex: NonEmptyLazyList[(A, Int)] = + create(toLazyList.zipWithIndex) - def apply[A](nev: NonEmptyLazyList[A]): ZipNonEmptyLazyList[A] = - new ZipNonEmptyLazyList(nev) + /** + * Groups elements inside this `NonEmptyLazyList` according to the `Order` + * of the keys produced by the given mapping function. + */ + final def groupBy[B](f: A => B): NonEmptyMap[B, NonEmptyLazyList[A]] = + toLazyList.groupBy(f).asInstanceOf[NonEmptyMap[B, NonEmptyLazyList[A]]] - 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(_))) + final def iterator: Iterator[A] = toLazyList.iterator - override def map[A, B](fa: ZipNonEmptyLazyList[A])(f: (A) => B): ZipNonEmptyLazyList[B] = - ZipNonEmptyLazyList(fa.value.map(f)) + final def reverseIterator: Iterator[A] = toLazyList.reverseIterator - 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) }) - } + /** + * Reverses this `NonEmptyLazyList` + */ + final def reverse: NonEmptyLazyList[A] = + create(toLazyList.reverse) - implicit def zipNelEq[A: Eq]: Eq[ZipNonEmptyLazyList[A]] = Eq.by(_.value) - } + /** + * Remove duplicates. + */ + final def distinct: NonEmptyLazyList[A] = + create(toLazyList.distinct) } -sealed abstract private[data] class NonEmptyLazyListInstances extends NonEmptyLazyListInstances0 { +sealed abstract private[data] class NonEmptyLazyListInstances extends NonEmptyLazyListInstances1 { implicit val catsDataInstancesForNonEmptyLazyList: SemigroupK[NonEmptyLazyList] with Reducible[NonEmptyLazyList] @@ -514,90 +344,70 @@ sealed abstract private[data] class NonEmptyLazyListInstances extends NonEmptyLa with NonEmptyTraverse[NonEmptyLazyList] { def combineK[A](a: NonEmptyLazyList[A], b: NonEmptyLazyList[A]): NonEmptyLazyList[A] = - a.concatNel(b) + a ++ b - override def split[A](fa: NonEmptyLazyList[A]): (A, LazyList[A]) = (fa.head, fa.tail) - - override def reduceLeft[A](fa: NonEmptyLazyList[A])(f: (A, A) => A): A = - fa.reduceLeft(f) - - override def reduce[A](fa: NonEmptyLazyList[A])(implicit A: Semigroup[A]): A = - fa.reduce - - override def map[A, B](fa: NonEmptyLazyList[A])(f: A => B): NonEmptyLazyList[B] = - fa.map(f) - - def pure[A](x: A): NonEmptyLazyList[A] = - NonEmptyLazyList.one(x) + def pure[A](x: A): NonEmptyLazyList[A] = NonEmptyLazyList(x) def flatMap[A, B](fa: NonEmptyLazyList[A])(f: A => NonEmptyLazyList[B]): NonEmptyLazyList[B] = fa.flatMap(f) - def coflatMap[A, B](fa: NonEmptyLazyList[A])(f: NonEmptyLazyList[A] => B): NonEmptyLazyList[B] = - fa.coflatMap(f) + // TODO does this work? + def tailRecM[A, B](a: A)(f: A => NonEmptyLazyList[Either[A, B]]): NonEmptyLazyList[B] = + create(Monad[LazyList].tailRecM(a)(a => unwrap(f(a)))) - def extract[A](fa: NonEmptyLazyList[A]): A = fa.head + def extract[A](x: NonEmptyLazyList[A]): A = x.head - def nonEmptyTraverse[G[_], A, B](nell: NonEmptyLazyList[A])(f: A => G[B])(implicit G: Apply[G]): G[NonEmptyLazyList[B]] = - Foldable[List] - .reduceRightToOption[A, G[List[B]]](nell.tail)(a => G.map(f(a))(_ #:: LazyList())) { (a, lglb) => - G.map2Eval(f(a), lglb)(_ #:: _) + // TODO preserve laziness + def coflatMap[A, B](fa: NonEmptyLazyList[A])(f: NonEmptyLazyList[A] => B): NonEmptyLazyList[B] = { + @tailrec def go(as: LazyList[A], res: ListBuffer[B]): LazyList[B] = + as.uncons match { + case Some((h, t)) => go(t, res += f(NonEmptyLazyList.fromLazyListPrepend(h, t))) + case None => LazyList.fromSeq(res.result()) + } + NonEmptyLazyList.fromLazyListPrepend(f(fa), go(fa.tail, ListBuffer.empty)) + } + + def nonEmptyTraverse[G[_]: Apply, A, B](fa: NonEmptyLazyList[A])(f: A => G[B]): G[NonEmptyLazyList[B]] = + Foldable[LazyList] + .reduceRightToOption[A, G[LazyList[B]]](fa.tail)(a => Apply[G].map(f(a))(Lazylist.one)) { (a, lglb) => + Apply[G].map2Eval(f(a), lglb)(_ +: _) } .map { - case None => G.map(f(nell.head))(NonEmptyLazyList(_, LazyList())) - case Some(gtail) => G.map2(f(nell.head), gtail)(NonEmptyLazyList(_, _)) + case None => Apply[G].map(f(fa.head))(NonEmptyLazyList.apply) + case Some(gtail) => Apply[G].map2(f(fa.head), gtail)((h, t) => create(NonEmptyLazyList(h) ++ t)) } .value - override def traverse[G[_], A, B](fa: NonEmptyLazyList[A])(f: A => G[B])(implicit G: Applicative[G]): G[NonEmptyLazyList[B]] = - fa.traverse(f) + override def map[A, B](fa: NonEmptyLazyList[A])(f: A => B): NonEmptyLazyList[B] = + create(fa.toLazyList.map(f)) + + override def size[A](fa: NonEmptyLazyList[A]): Long = fa.length + + override def reduceLeft[A](fa: NonEmptyLazyList[A])(f: (A, A) => A): A = + fa.reduceLeft(f) + + override def reduce[A](fa: NonEmptyLazyList[A])(implicit A: Semigroup[A]): A = + fa.reduce + + def reduceLeftTo[A, B](fa: NonEmptyLazyList[A])(f: A => B)(g: (B, A) => B): B = fa.reduceLeftTo(f)(g) + + def reduceRightTo[A, B](fa: NonEmptyLazyList[A])(f: A => B)(g: (A, Eval[B]) => Eval[B]): Eval[B] = + Eval.defer(fa.reduceRightTo(a => Eval.now(f(a))) { (a, b) => + Eval.defer(g(a, b)) + }) override def foldLeft[A, B](fa: NonEmptyLazyList[A], b: B)(f: (B, A) => B): B = fa.foldLeft(b)(f) override def foldRight[A, B](fa: NonEmptyLazyList[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = - fa.foldRight(lb)(f) + Foldable[LazyList].foldRight(fa.toLazyList, lb)(f) override def foldMap[A, B](fa: NonEmptyLazyList[A])(f: A => B)(implicit B: Monoid[B]): B = B.combineAll(fa.toLazyList.iterator.map(f)) - // TODO *** Builder - def tailRecM[A, B](a: A)(f: A => NonEmptyLazyList[Either[A, B]]): NonEmptyLazyList[B] = { - val buf = new ListBuffer[B] - @tailrec def go(v: NonEmptyLazyList[Either[A, B]]): Unit = v.head match { - case Right(b) => - buf += b - NonEmptyLazyList.fromList(v.tail) match { - case Some(t) => go(t) - case None => () - } - case Left(a) => go(f(a) ++ v.tail) - } - go(f(a)) - NonEmptyLazyList.fromListUnsafe(buf.result()) - } - override def fold[A](fa: NonEmptyLazyList[A])(implicit A: Monoid[A]): A = fa.reduce - override def nonEmptyPartition[A, B, C](fa: NonEmptyLazyList[A])(f: (A) => Either[B, C]): Ior[NonEmptyLazyList[B], NonEmptyLazyList[C]] = { - import cats.syntax.either._ - - val reversed = fa.reverse - val lastIor = f(reversed.head).bimap(NonEmptyLazyList.one, NonEmptyLazyList.one).toIor - - reversed.tail.foldLeft(lastIor)( - (ior, a) => - (f(a), ior) match { - case (Right(c), Ior.Left(_)) => ior.putRight(NonEmptyLazyList.one(c)) - case (Right(c), _) => ior.map(c #:: _) - case (Left(b), Ior.Right(r)) => Ior.bothNel(b, r) - case (Left(b), _) => ior.leftMap(b #:: _) - } - ) - - } - override def find[A](fa: NonEmptyLazyList[A])(f: A => Boolean): Option[A] = fa.find(f) @@ -609,72 +419,36 @@ sealed abstract private[data] class NonEmptyLazyListInstances extends NonEmptyLa override def toList[A](fa: NonEmptyLazyList[A]): List[A] = fa.toLazyList.toList - override def toNonEmptyLazyList[A](fa: NonEmptyLazyList[A]): NonEmptyLazyList[A] = fa + override def toNonEmptyList[A](fa: NonEmptyLazyList[A]): NonEmptyList[A] = + fa.toNonEmptyList - override def get[A](fa: NonEmptyLazyList[A])(idx: Long): Option[A] = - if (idx == 0) Some(fa.head) else Foldable[List].get(fa.tail)(idx - 1) - } + override def collectFirst[A, B](fa: NonEmptyLazyList[A])(pf: PartialFunction[A, B]): Option[B] = + fa.collectFirst(pf) - implicit def catsDataShowForNonEmptyLazyList[A](implicit A: Show[A]): Show[NonEmptyLazyList[A]] = - Show.show[NonEmptyLazyList[A]](_.show) - - implicit def catsDataSemigroupForNonEmptyLazyList[A]: Semigroup[NonEmptyLazyList[A]] = - SemigroupK[NonEmptyLazyList].algebra[A] - - implicit def catsDataOrderForNonEmptyLazyList[A](implicit A: Order[A]): Order[NonEmptyLazyList[A]] = - new NonEmptyLazyListOrder[A] { - val A0 = A + override def collectFirstSome[A, B](fa: NonEmptyLazyList[A])(f: A => Option[B]): Option[B] = + fa.collectFirstSome(f) } - implicit def catsDataNonEmptyParallelForNonEmptyLazyList[A]: NonEmptyParallel[NonEmptyLazyList, ZipNonEmptyLazyList] = - new NonEmptyParallel[NonEmptyLazyList, ZipNonEmptyLazyList] { - - def flatMap: FlatMap[NonEmptyLazyList] = NonEmptyLazyList.catsDataInstancesForNonEmptyLazyList + implicit def catsDataOrderForNonEmptyLazyList[A: Order]: Order[NonEmptyLazyList[A]] = + Order.by[NonEmptyLazyList[A], LazyList[A]](_.toLazyList) - def apply: Apply[ZipNonEmptyLazyList] = ZipNonEmptyLazyList.catsDataCommutativeApplyForZipNonEmptyLazyList - - def sequential: ZipNonEmptyLazyList ~> NonEmptyLazyList = - λ[ZipNonEmptyLazyList ~> NonEmptyLazyList](_.value) + implicit def catsDataShowForNonEmptyLazyList[A](implicit A: Show[A]): Show[NonEmptyLazyList[A]] = + Show.show[NonEmptyLazyList[A]](nec => s"NonEmpty${Show[LazyList[A]].show(nec.toLazyList)}") - def parallel: NonEmptyLazyList ~> ZipNonEmptyLazyList = - λ[NonEmptyLazyList ~> ZipNonEmptyLazyList](nell => new ZipNonEmptyLazyList(nell)) - } + implicit def catsDataSemigroupForNonEmptyLazyList[A]: Semigroup[NonEmptyLazyList[A]] = new Semigroup[NonEmptyLazyList[A]] { + def combine(x: NonEmptyLazyList[A], y: NonEmptyLazyList[A]): NonEmptyLazyList[A] = x ++ y + } } -sealed abstract private[data] class NonEmptyLazyListInstances0 extends NonEmptyLazyListInstances1 { - implicit def catsDataPartialOrderForNonEmptyLazyList[A](implicit A: PartialOrder[A]): PartialOrder[NonEmptyLazyList[A]] = - new NonEmptyLazyListPartialOrder[A] { - val A0 = A - } +sealed abstract private[data] class NonEmptyLazyListInstances1 extends NonEmptyLazyListInstances2 { + implicit def catsDataPartialOrderForNonEmptyLazyList[A: PartialOrder]: PartialOrder[NonEmptyLazyList[A]] = + PartialOrder.by[NonEmptyLazyList[A], LazyList[A]](_.toLazyList) } -sealed abstract private[data] class NonEmptyLazyListInstances1 { - - implicit def catsDataEqForNonEmptyLazyList[A](implicit A: Eq[A]): Eq[NonEmptyLazyList[A]] = - new NonEmptyLazyListEq[A] { - val A0 = A +sealed abstract private[data] class NonEmptyLazyListInstances2 { + implicit def catsDataEqForNonEmptyLazyList[A: Eq]: Eq[NonEmptyLazyList[A]] = + new Eq[NonEmptyLazyList[A]] { + def eqv(x: NonEmptyLazyList[A], y: NonEmptyLazyList[A]): Boolean = x.toLazyList === y.toLazyList } } -sealed private[data] trait NonEmptyLazyListEq[A] extends Eq[NonEmptyLazyList[A]] { - implicit def A0: Eq[A] - - override def eqv(x: NonEmptyLazyList[A], y: NonEmptyLazyList[A]): Boolean = x === y -} - -sealed private[data] trait NonEmptyLazyListPartialOrder[A] extends PartialOrder[NonEmptyLazyList[A]] with NonEmptyLazyListEq[A] { - implicit override def A0: PartialOrder[A] - - override def partialCompare(x: NonEmptyLazyList[A], y: NonEmptyLazyList[A]): Double = - x.toLazyList.partialCompare(y.toLazyList) -} - -sealed abstract private[data] class NonEmptyLazyListOrder[A] - extends Order[NonEmptyLazyList[A]] - with NonEmptyLazyListPartialOrder[A] { - implicit override def A0: Order[A] - - override def compare(x: NonEmptyLazyList[A], y: NonEmptyLazyList[A]): Int = - x.toLazyList.compare(y.toLazyList) -} - From 3608e59a8a1f0da1db85b1d3f282dc75f090d292 Mon Sep 17 00:00:00 2001 From: Nathaniel May Date: Fri, 28 Jun 2019 12:15:11 -0400 Subject: [PATCH 08/12] fixed imports --- core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala index 785c03d3ae..45e9dbbde0 100644 --- a/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala +++ b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala @@ -3,7 +3,8 @@ package cats.data import NonEmptyLazyListImpl.{NonEmptyLazyList, create, unwrap} import cats.{Apply, Bimonad, Eval, Foldable, Monad, NonEmptyReducible, NonEmptyTraverse, Order, Reducible, SemigroupK, Show} import cats.kernel.{Order, _} - +import cats.instances.lazyList._ +import cats.instances.order._ import scala.annotation.tailrec import scala.collection.immutable._ import scala.collection.mutable.ListBuffer From e7717c1a1beaa1f88da868347a7a604601cc6f22 Mon Sep 17 00:00:00 2001 From: Nathaniel May Date: Fri, 28 Jun 2019 15:39:40 -0400 Subject: [PATCH 09/12] removed collectFirstSome. Defers to default instance impl --- .../main/scala-2.13+/cats/data/NonEmptyLazyList.scala | 9 --------- 1 file changed, 9 deletions(-) diff --git a/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala index 45e9dbbde0..2758e8da56 100644 --- a/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala +++ b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala @@ -224,15 +224,6 @@ class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A]) extends Any */ final def collectLazyList[B](pf: PartialFunction[A, B]): Option[B] = toLazyList.collectFirst(pf) - /** - * Like `collectFirst` from `scala.collection.Traversable` but takes `A => Option[B]` - * instead of `PartialFunction`s. - */ // TODO manually implemented because LazyList doesn't have collectFirstSome - final def collectFirstSome[B](f: A => Option[B]): Option[B] = - collectLazyList(PartialFunction[A, B] { - case a: A if f(a).isDefined => f(a).get - }) - /** * Filters all elements of this NonEmptyLazyList that do not satisfy the given predicate. */ From 715b0dda134233ac239f76cb79fa07898b38c267 Mon Sep 17 00:00:00 2001 From: Nathaniel May Date: Fri, 28 Jun 2019 15:42:27 -0400 Subject: [PATCH 10/12] reduceLeftTo and reduceRightTo now uses same impl from NonEmptyChain --- .../cats/data/NonEmptyLazyList.scala | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala index 2758e8da56..d69feb97aa 100644 --- a/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala +++ b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala @@ -255,9 +255,13 @@ class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A]) extends Any /** * Apply `f` to the "initial element" of this LazyList and lazily combine it * with every other value using the given function `g`. - */ // TODO not implemented in LazyList - final def reduceLeftTo[B](f: A => B)(g: (B, A) => B): B = - toLazyList.reduceLeftTo(f)(g) + */ + final def reduceLeftTo[B](f: A => B)(g: (B, A) => B): B = { + val iter = toLazyList.iterator + var result = f(iter.next) + while (iter.hasNext) { result = g(result, iter.next) } + result + } /** @@ -269,9 +273,13 @@ class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A]) extends Any /** * Apply `f` to the "initial element" of this NonEmptyLazyList and * lazily combine it with every other value using the given function `g`. - */ // TODO not implemented in LazyList - final def reduceRightTo[B](f: A => B)(g: (A, B) => B): B = - toLazyList.reduceRightTo(f)(g) + */ + final def reduceRightTo[B](f: A => B)(g: (A, B) => B): B = { + val iter = toLazyList.reverseIterator + var result = f(iter.next) + while (iter.hasNext) { result = g(iter.next, result) } + result + } /** * Reduce using the Semigroup of A From 5cee9d3c839315ad6f1f22d1cb98446692e33fba Mon Sep 17 00:00:00 2001 From: Nathaniel May Date: Fri, 28 Jun 2019 16:04:33 -0400 Subject: [PATCH 11/12] refactored instances to pull from existing LazyList instances --- .../cats/data/NonEmptyLazyList.scala | 127 +++--------------- 1 file changed, 20 insertions(+), 107 deletions(-) diff --git a/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala index d69feb97aa..16f003631b 100644 --- a/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala +++ b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala @@ -334,121 +334,34 @@ class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A]) extends Any create(toLazyList.distinct) } -sealed abstract private[data] class NonEmptyLazyListInstances extends NonEmptyLazyListInstances1 { +sealed abstract private[data] class NonEmptyLazyListInstances extends NonEmptyLazyListInstances1 { - implicit val catsDataInstancesForNonEmptyLazyList: SemigroupK[NonEmptyLazyList] - with Reducible[NonEmptyLazyList] - with Bimonad[NonEmptyLazyList] - with NonEmptyTraverse[NonEmptyLazyList] = - new NonEmptyReducible[NonEmptyLazyList, LazyList] with SemigroupK[NonEmptyLazyList] with Bimonad[NonEmptyLazyList] - with NonEmptyTraverse[NonEmptyLazyList] { + implicit val catsDataBimonadForNonEmptyLazyList: Bimonad[NonEmptyLazyList] = Bimonad[LazyList].asInstanceOf[Bimonad[NonEmptyLazyList]] - def combineK[A](a: NonEmptyLazyList[A], b: NonEmptyLazyList[A]): NonEmptyLazyList[A] = - a ++ b - - def pure[A](x: A): NonEmptyLazyList[A] = NonEmptyLazyList(x) - - def flatMap[A, B](fa: NonEmptyLazyList[A])(f: A => NonEmptyLazyList[B]): NonEmptyLazyList[B] = - fa.flatMap(f) - - // TODO does this work? - def tailRecM[A, B](a: A)(f: A => NonEmptyLazyList[Either[A, B]]): NonEmptyLazyList[B] = - create(Monad[LazyList].tailRecM(a)(a => unwrap(f(a)))) - - def extract[A](x: NonEmptyLazyList[A]): A = x.head - - // TODO preserve laziness - def coflatMap[A, B](fa: NonEmptyLazyList[A])(f: NonEmptyLazyList[A] => B): NonEmptyLazyList[B] = { - @tailrec def go(as: LazyList[A], res: ListBuffer[B]): LazyList[B] = - as.uncons match { - case Some((h, t)) => go(t, res += f(NonEmptyLazyList.fromLazyListPrepend(h, t))) - case None => LazyList.fromSeq(res.result()) - } - NonEmptyLazyList.fromLazyListPrepend(f(fa), go(fa.tail, ListBuffer.empty)) - } - - def nonEmptyTraverse[G[_]: Apply, A, B](fa: NonEmptyLazyList[A])(f: A => G[B]): G[NonEmptyLazyList[B]] = - Foldable[LazyList] - .reduceRightToOption[A, G[LazyList[B]]](fa.tail)(a => Apply[G].map(f(a))(Lazylist.one)) { (a, lglb) => - Apply[G].map2Eval(f(a), lglb)(_ +: _) - } - .map { - case None => Apply[G].map(f(fa.head))(NonEmptyLazyList.apply) - case Some(gtail) => Apply[G].map2(f(fa.head), gtail)((h, t) => create(NonEmptyLazyList(h) ++ t)) - } - .value - - override def map[A, B](fa: NonEmptyLazyList[A])(f: A => B): NonEmptyLazyList[B] = - create(fa.toLazyList.map(f)) - - override def size[A](fa: NonEmptyLazyList[A]): Long = fa.length - - override def reduceLeft[A](fa: NonEmptyLazyList[A])(f: (A, A) => A): A = - fa.reduceLeft(f) - - override def reduce[A](fa: NonEmptyLazyList[A])(implicit A: Semigroup[A]): A = - fa.reduce - - def reduceLeftTo[A, B](fa: NonEmptyLazyList[A])(f: A => B)(g: (B, A) => B): B = fa.reduceLeftTo(f)(g) - - def reduceRightTo[A, B](fa: NonEmptyLazyList[A])(f: A => B)(g: (A, Eval[B]) => Eval[B]): Eval[B] = - Eval.defer(fa.reduceRightTo(a => Eval.now(f(a))) { (a, b) => - Eval.defer(g(a, b)) - }) - - override def foldLeft[A, B](fa: NonEmptyLazyList[A], b: B)(f: (B, A) => B): B = - fa.foldLeft(b)(f) - - override def foldRight[A, B](fa: NonEmptyLazyList[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = - Foldable[LazyList].foldRight(fa.toLazyList, lb)(f) - - override def foldMap[A, B](fa: NonEmptyLazyList[A])(f: A => B)(implicit B: Monoid[B]): B = - B.combineAll(fa.toLazyList.iterator.map(f)) - - override def fold[A](fa: NonEmptyLazyList[A])(implicit A: Monoid[A]): A = - fa.reduce - - override def find[A](fa: NonEmptyLazyList[A])(f: A => Boolean): Option[A] = - fa.find(f) - - override def forall[A](fa: NonEmptyLazyList[A])(p: A => Boolean): Boolean = - fa.forall(p) - - override def exists[A](fa: NonEmptyLazyList[A])(p: A => Boolean): Boolean = - fa.exists(p) - - override def toList[A](fa: NonEmptyLazyList[A]): List[A] = fa.toLazyList.toList - - override def toNonEmptyList[A](fa: NonEmptyLazyList[A]): NonEmptyList[A] = - fa.toNonEmptyList - - override def collectFirst[A, B](fa: NonEmptyLazyList[A])(pf: PartialFunction[A, B]): Option[B] = - fa.collectFirst(pf) - - override def collectFirstSome[A, B](fa: NonEmptyLazyList[A])(f: A => Option[B]): Option[B] = - fa.collectFirstSome(f) - } +} - implicit def catsDataOrderForNonEmptyLazyList[A: Order]: Order[NonEmptyLazyList[A]] = - Order.by[NonEmptyLazyList[A], LazyList[A]](_.toLazyList) +sealed abstract private[data] class NonEmptyLazyListInstances1 extends NonEmptyLazyListInstances2 { - implicit def catsDataShowForNonEmptyLazyList[A](implicit A: Show[A]): Show[NonEmptyLazyList[A]] = - Show.show[NonEmptyLazyList[A]](nec => s"NonEmpty${Show[LazyList[A]].show(nec.toLazyList)}") + implicit val catsDataSemigroupKForNonEmptyLazyList: SemigroupK[NonEmptyLazyList] = SemigroupK[LazyList].asInstanceOf[SemigroupK[NonEmptyLazyList]] - implicit def catsDataSemigroupForNonEmptyLazyList[A]: Semigroup[NonEmptyLazyList[A]] = new Semigroup[NonEmptyLazyList[A]] { - def combine(x: NonEmptyLazyList[A], y: NonEmptyLazyList[A]): NonEmptyLazyList[A] = x ++ y - } -} - -sealed abstract private[data] class NonEmptyLazyListInstances1 extends NonEmptyLazyListInstances2 { implicit def catsDataPartialOrderForNonEmptyLazyList[A: PartialOrder]: PartialOrder[NonEmptyLazyList[A]] = - PartialOrder.by[NonEmptyLazyList[A], LazyList[A]](_.toLazyList) + PartialOrder[LazyList[A]].asInstanceOf[PartialOrder[NonEmptyLazyList[A]]] } sealed abstract private[data] class NonEmptyLazyListInstances2 { - implicit def catsDataEqForNonEmptyLazyList[A: Eq]: Eq[NonEmptyLazyList[A]] = - new Eq[NonEmptyLazyList[A]] { - def eqv(x: NonEmptyLazyList[A], y: NonEmptyLazyList[A]): Boolean = x.toLazyList === y.toLazyList - } + implicit val catsDataNonEmptyTraverseForNonEmptyLazyList: NonEmptyTraverse[LazyList] = new NonEmptyTraverse[LazyList] { + val traverseInstance = Traverse[LazyList].asInstanceOf[Traverse[NonEmptyLazyList]] + //delegate all traverse methods to traverseInstance + + def nonEmptyTraverse[G[_]: Apply, A, B](fa: NonEmptyLazyList[A])(f: A => G[B]): G[NonEmptyLazyList[B]] = + Foldable[LazyList].reduceRightToOption[A, G[LazyList[B]]](fa.tail)(a => Apply[G].map(f(a))(Lazylist.apply(_))) { (a, lglb) => + Apply[G].map2Eval(f(a), lglb)(_ +: _) + }.map { + case None => Apply[G].map(f(fa.head))(NonEmptyLazyList.apply) + case Some(gtail) => Apply[G].map2(f(fa.head), gtail)((h, t) => create(NonEmptyLazyList(h) ++ t)) + } .value + } + + implicit def catsDataEqForNonEmptyLazyList[A: Eq]: Eq[NonEmptyLazyList[A]] = Eq[LazyList[A]].asInstanceOf[PartialOrder[NonEmptyLazyList]] } From 1ff130b8ea58dcb7e10930b09ad2ebf3144242f0 Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Fri, 28 Jun 2019 17:18:52 -0400 Subject: [PATCH 12/12] fix compilation --- .../cats/data/NonEmptyLazyImpl.scala | 5 + .../cats/data/NonEmptyLazyList.scala | 103 ++++++++++++++---- core/src/main/scala/cats/data/package.scala | 5 + 3 files changed, 90 insertions(+), 23 deletions(-) create mode 100644 core/src/main/scala-2.12-/cats/data/NonEmptyLazyImpl.scala diff --git a/core/src/main/scala-2.12-/cats/data/NonEmptyLazyImpl.scala b/core/src/main/scala-2.12-/cats/data/NonEmptyLazyImpl.scala new file mode 100644 index 0000000000..0377d7dc9d --- /dev/null +++ b/core/src/main/scala-2.12-/cats/data/NonEmptyLazyImpl.scala @@ -0,0 +1,5 @@ +package cats.data + +object NonEmptyLazyImpl { + type Type[+A] = NonEmptyStream[A] +} diff --git a/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala index 16f003631b..07193bad20 100644 --- a/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala +++ b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala @@ -1,18 +1,15 @@ -package cats.data +package cats +package data + +import NonEmptyLazyListImpl.{create, NonEmptyLazyList} +import kernel.PartialOrder +import instances.lazyList._ + -import NonEmptyLazyListImpl.{NonEmptyLazyList, create, unwrap} -import cats.{Apply, Bimonad, Eval, Foldable, Monad, NonEmptyReducible, NonEmptyTraverse, Order, Reducible, SemigroupK, Show} -import cats.kernel.{Order, _} -import cats.instances.lazyList._ -import cats.instances.order._ -import scala.annotation.tailrec -import scala.collection.immutable._ -import scala.collection.mutable.ListBuffer private[data] object NonEmptyLazyListImpl extends NonEmptyLazyListInstances { // TODO belongs in package object: type NonEmptyLazyList[+A] = NonEmptyLazyListImpl.Type[A] - //val NonEmptyLazyList = NonEmptyChainImpl private[data] type Base private[data] trait Tag extends Any @@ -60,6 +57,8 @@ class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A]) extends Any */ final def toLazyList: LazyList[A] = NonEmptyLazyListImpl.unwrap(value) + final def map[B](f: A => B): NonEmptyLazyList[B] = create(toLazyList.map(f)) + /** * Returns the last element */ @@ -78,7 +77,7 @@ class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A]) extends Any /** * Returns the length of this NonEmptyLazyList */ - final def length: Int = size + final def length: Int = toLazyList.length /** * Returns a new NonEmptyLazyList consisting of `a` followed by this @@ -293,11 +292,6 @@ class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A]) extends Any final def flatMap[B](f: A => NonEmptyLazyList[B]): NonEmptyLazyList[B] = create(toLazyList.flatMap(f.andThen(_.toLazyList))) - /** - * Returns the number of elements - */ - final def length: Long = toLazyList.size - /** * Zips this `NonEmptyLazyList` with another `NonEmptyLazyList` and applies a function for each pair of elements */ // TODO manually implemented @@ -336,7 +330,30 @@ class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A]) extends Any sealed abstract private[data] class NonEmptyLazyListInstances extends NonEmptyLazyListInstances1 { - implicit val catsDataBimonadForNonEmptyLazyList: Bimonad[NonEmptyLazyList] = Bimonad[LazyList].asInstanceOf[Bimonad[NonEmptyLazyList]] + implicit val catsDataBimonadForNonEmptyLazyList: Bimonad[NonEmptyLazyList] = new Bimonad[NonEmptyLazyList] { + val monadInstance = Monad[LazyList].asInstanceOf[Monad[NonEmptyLazyList]] + val coflatMapInstance = CoflatMap[LazyList].asInstanceOf[CoflatMap[NonEmptyLazyList]] + + def extract[A](fa: NonEmptyLazyList[A]): A = fa.head + + def pure[A](x: A): NonEmptyLazyList[A] = monadInstance.pure(x) + + override def map[A, B](fa: NonEmptyLazyList[A])(f: A => B): NonEmptyLazyList[B] = fa.map(f) + + def flatMap[A, B](fa: NonEmptyLazyList[A])(f: A => NonEmptyLazyList[B]): NonEmptyLazyList[B] = + fa.flatMap(f) + + override def map2[A, B, Z](fa: NonEmptyLazyList[A], fb: NonEmptyLazyList[B])(f: (A, B) => Z): NonEmptyLazyList[Z] = + monadInstance.map2(fa, fb)(f) + + override def map2Eval[A, B, Z](fa: NonEmptyLazyList[A], fb: Eval[NonEmptyLazyList[B]])(f: (A, B) => Z): Eval[NonEmptyLazyList[Z]] = + monadInstance.map2Eval(fa, fb)(f) + + def coflatMap[A, B](fa: NonEmptyLazyList[A])(f: NonEmptyLazyList[A] => B): NonEmptyLazyList[B] = + coflatMapInstance.coflatMap(fa)(f) + + def tailRecM[A, B](a: A)(f: A => NonEmptyLazyList[Either[A,B]]): NonEmptyLazyList[B] = monadInstance.tailRecM(a)(f) + } } @@ -349,19 +366,59 @@ sealed abstract private[data] class NonEmptyLazyListInstances1 extends NonEmpty } sealed abstract private[data] class NonEmptyLazyListInstances2 { - implicit val catsDataNonEmptyTraverseForNonEmptyLazyList: NonEmptyTraverse[LazyList] = new NonEmptyTraverse[LazyList] { + implicit val catsDataNonEmptyTraverseForNonEmptyLazyList: NonEmptyTraverse[NonEmptyLazyList] = new NonEmptyTraverse[NonEmptyLazyList] { val traverseInstance = Traverse[LazyList].asInstanceOf[Traverse[NonEmptyLazyList]] - //delegate all traverse methods to traverseInstance def nonEmptyTraverse[G[_]: Apply, A, B](fa: NonEmptyLazyList[A])(f: A => G[B]): G[NonEmptyLazyList[B]] = - Foldable[LazyList].reduceRightToOption[A, G[LazyList[B]]](fa.tail)(a => Apply[G].map(f(a))(Lazylist.apply(_))) { (a, lglb) => + Foldable[LazyList].reduceRightToOption[A, G[LazyList[B]]](fa.tail)(a => Apply[G].map(f(a))(LazyList.apply(_))) { (a, lglb) => Apply[G].map2Eval(f(a), lglb)(_ +: _) }.map { - case None => Apply[G].map(f(fa.head))(NonEmptyLazyList.apply) - case Some(gtail) => Apply[G].map2(f(fa.head), gtail)((h, t) => create(NonEmptyLazyList(h) ++ t)) + case None => Apply[G].map(f(fa.head))(h => create(LazyList(h))) + case Some(gtail) => Apply[G].map2(f(fa.head), gtail)((h, t) => create(LazyList(h) ++ t)) } .value + + def foldLeft[A, B](fa: NonEmptyLazyList[A], b: B)(f: (B, A) => B): B = traverseInstance.foldLeft(fa, b)(f) + + def foldRight[A, B](fa: NonEmptyLazyList[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = traverseInstance.foldRight(fa, lb)(f) + + def reduceLeftTo[A, B](fa: NonEmptyLazyList[A])(f: A => B)(g: (B, A) => B): B = fa.reduceLeftTo(f)(g) + + def reduceRightTo[A, B](fa: NonEmptyLazyList[A])(f: A => B)(g: (A, cats.Eval[B]) => cats.Eval[B]): cats.Eval[B] = + Eval.defer(fa.reduceRightTo(a => Eval.now(f(a))) { (a, b) => + Eval.defer(g(a, b)) + }) + + override def foldMap[A, B](fa: NonEmptyLazyList[A])(f: A => B)(implicit B: Monoid[B]): B = traverseInstance.foldMap(fa)(f) + + override def traverse[G[_], A, B](fa: NonEmptyLazyList[A])(f: A => G[B])(implicit G: Applicative[G]): G[NonEmptyLazyList[B]] = traverseInstance.traverse(fa)(f) + + override def mapWithIndex[A, B](fa: NonEmptyLazyList[A])(f: (A, Int) => B): NonEmptyLazyList[B] = traverseInstance.mapWithIndex(fa)(f) + + override def zipWithIndex[A](fa: NonEmptyLazyList[A]): NonEmptyLazyList[(A, Int)] = fa.zipWithIndex + + override def exists[A](fa: NonEmptyLazyList[A])(p: A => Boolean): Boolean = fa.exists(p) + + override def forall[A](fa: NonEmptyLazyList[A])(p: A => Boolean): Boolean = fa.forall(p) + + override def get[A](fa: NonEmptyLazyList[A])(idx: Long): Option[A] = traverseInstance.get(fa)(idx) + + override def isEmpty[A](fa: NonEmptyLazyList[A]): Boolean = false + + override def foldM[G[_], A, B](fa: NonEmptyLazyList[A], z: B)(f: (B, A) => G[B])(implicit G: Monad[G]): G[B] = traverseInstance.foldM(fa, z)(f) + + override def fold[A](fa: NonEmptyLazyList[A])(implicit A: Monoid[A]): A = traverseInstance.fold(fa) + + override def toList[A](fa: NonEmptyLazyList[A]): List[A] = traverseInstance.toList(fa) + + override def reduceLeftOption[A](fa: NonEmptyLazyList[A])(f: (A, A) => A): Option[A] = traverseInstance.reduceLeftOption(fa)(f) + + override def find[A](fa: NonEmptyLazyList[A])(f: A => Boolean): Option[A] = fa.find(f) + + override def collectFirst[A, B](fa: NonEmptyLazyList[A])(pf: PartialFunction[A, B]): Option[B] = traverseInstance.collectFirst(fa)(pf) + + override def collectFirstSome[A, B](fa: NonEmptyLazyList[A])(f: A => Option[B]): Option[B] = traverseInstance.collectFirstSome(fa)(f) } - implicit def catsDataEqForNonEmptyLazyList[A: Eq]: Eq[NonEmptyLazyList[A]] = Eq[LazyList[A]].asInstanceOf[PartialOrder[NonEmptyLazyList]] + implicit def catsDataEqForNonEmptyLazyList[A: Eq]: Eq[NonEmptyLazyList[A]] = Eq[LazyList[A]].asInstanceOf[PartialOrder[NonEmptyLazyList[A]]] } diff --git a/core/src/main/scala/cats/data/package.scala b/core/src/main/scala/cats/data/package.scala index 09473cbba7..b01b9dac85 100644 --- a/core/src/main/scala/cats/data/package.scala +++ b/core/src/main/scala/cats/data/package.scala @@ -1,4 +1,5 @@ package cats +import cats.data.NonEmptyLazyListImpl import kernel.compat.scalaVersionSpecific._ import compat.lazyList.toLazyList package object data { @@ -13,6 +14,10 @@ package object data { type EitherNes[E, +A] = Either[NonEmptySet[E], A] type ValidatedNec[+E, +A] = Validated[NonEmptyChain[E], A] + + type NonEmptyLazyList[+A] = NonEmptyLazyListImpl.Type[A] + val NonEmptyLazyList = NonEmptyLazyListImpl + def NonEmptyStream[A](head: A, tail: LazyList[A] = LazyList.empty): NonEmptyStream[A] = OneAnd(head, tail) def NonEmptyStream[A](head: A, tail: A*): NonEmptyStream[A] =