From ef284ec1617952e9c575ba5561889b723c7e934b Mon Sep 17 00:00:00 2001 From: Marissa/Princess Date: Thu, 4 May 2023 12:09:15 -0400 Subject: [PATCH 01/12] Make `NonEmptyLazyList` properly lazy Make `NonEmptyLazyList` properly lazy and remove unnecessary laziness. --- .../cats/data/NonEmptyLazyList.scala | 14 ++++++------- .../cats/tests/NonEmptyLazyListSuite.scala | 20 +++++++++++++++++++ 2 files changed, 27 insertions(+), 7 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 846b960ef1..746d115f68 100644 --- a/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala +++ b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala @@ -47,7 +47,7 @@ object NonEmptyLazyList extends NonEmptyLazyListInstances { s.asInstanceOf[LazyList[A]] def fromLazyList[A](as: LazyList[A]): Option[NonEmptyLazyList[A]] = - if (as.nonEmpty) Option(create(as)) else None + if (as.nonEmpty) Some(create(as)) else None def fromLazyListUnsafe[A](ll: LazyList[A]): NonEmptyLazyList[A] = if (ll.nonEmpty) create(ll) @@ -69,7 +69,7 @@ object NonEmptyLazyList extends NonEmptyLazyListInstances { create(ca :+ a) def apply[A](a: => A, as: A*): NonEmptyLazyList[A] = - create(LazyList.concat(LazyList(a), LazyList.from(as))) + create(a #:: LazyList.from(as)) implicit def catsNonEmptyLazyListOps[A](value: NonEmptyLazyList[A]): NonEmptyLazyListOps[A] = new NonEmptyLazyListOps(value) @@ -110,7 +110,7 @@ class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A]) * Returns a new NonEmptyLazyList consisting of `a` followed by this */ final def prepend[AA >: A](a: AA): NonEmptyLazyList[AA] = - create(a #:: toLazyList) + create(toLazyList.prepended(a)) /** * Alias for [[prepend]]. @@ -121,6 +121,8 @@ class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A]) /** * Alias for [[prepend]]. */ + // TODO: `a` should be by-name and this method should not be listed as an + // alias for `prepend`, but it's too late to change that in this version final def #::[AA >: A](a: AA): NonEmptyLazyList[AA] = prepend(a) @@ -158,8 +160,7 @@ class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A]) * Appends the given LazyList */ final def appendLazyList[AA >: A](nell: LazyList[AA]): NonEmptyLazyList[AA] = - if (nell.isEmpty) value - else create(toLazyList ++ nell) + create(toLazyList ++ nell) /** * Alias for `appendLazyList` @@ -171,8 +172,7 @@ class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A]) * Prepends the given LazyList */ final def prependLazyList[AA >: A](c: LazyList[AA]): NonEmptyLazyList[AA] = - if (c.isEmpty) value - else create(c ++ toLazyList) + create(c ++ toLazyList) /** * Prepends the given NonEmptyLazyList diff --git a/tests/shared/src/test/scala-2.13+/cats/tests/NonEmptyLazyListSuite.scala b/tests/shared/src/test/scala-2.13+/cats/tests/NonEmptyLazyListSuite.scala index a4cd1352e5..675f3c58e4 100644 --- a/tests/shared/src/test/scala-2.13+/cats/tests/NonEmptyLazyListSuite.scala +++ b/tests/shared/src/test/scala-2.13+/cats/tests/NonEmptyLazyListSuite.scala @@ -199,6 +199,26 @@ class NonEmptyLazyListSuite extends NonEmptyCollectionSuite[LazyList, NonEmptyLa (1 to 100).sum === sum } + + test("NonEmptyLazyList#appendLazyList is properly lazy") { + var evaluated = false + val ll = { evaluated = true; 1 } #:: LazyList.empty + val _ = NonEmptyLazyList(0).appendLazyList(ll) + assert(!evaluated) + } + + test("NonEmptyLazyList#prependLazyList is properly lazy") { + var evaluated = false + val ll = { evaluated = true; 0 } #:: LazyList.empty + val _ = NonEmptyLazyList(1).prependLazyList(ll) + assert(!evaluated) + } + + test("NonEmptyLazyList.apply is properly lazy") { + var evaluated = false + val _ = NonEmptyLazyList({ evaluated = true; 0 }, Nil: _*) + assert(!evaluated) + } } class ReducibleNonEmptyLazyListSuite extends ReducibleSuite[NonEmptyLazyList]("NonEmptyLazyList") { From ad690650898c6cbc7c361b875e0dc8672615824c Mon Sep 17 00:00:00 2001 From: Marissa Date: Sun, 3 Sep 2023 01:40:17 -0400 Subject: [PATCH 02/12] Redesign `NonEmptyLazyList` to be maximally lazy This commit does not include docs or tests. --- .../cats/data/NonEmptyLazyList.scala | 106 +++++++++++++----- 1 file changed, 79 insertions(+), 27 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 746d115f68..c12e389539 100644 --- a/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala +++ b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala @@ -62,15 +62,47 @@ object NonEmptyLazyList extends NonEmptyLazyListInstances { 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 fromLazyListPrepend[A](a: => A, ll: => LazyList[A]): NonEmptyLazyList[A] = + create(a #:: ll) - def fromLazyListAppend[A](ca: LazyList[A], a: A): NonEmptyLazyList[A] = - create(ca :+ a) + def fromLazyListAppend[A](ll: => LazyList[A], a: => A): NonEmptyLazyList[A] = + create(ll #::: a #:: LazyList.empty) def apply[A](a: => A, as: A*): NonEmptyLazyList[A] = create(a #:: LazyList.from(as)) + // allows the creation of fully lazy `NonEmptyLazyList`s by prepending to this + def maybe[A](ll: => LazyList[A]): Maybe[A] = new Maybe(() => ll) + + final class Maybe[A] private[NonEmptyLazyList] (private[this] var mkLL: () => LazyList[A]) { + // because instances of this class are created explicitly, they might be + // reused, and we don't want to re-evaluate `mkLL` + private[this] lazy val ll = { + val res = mkLL() + mkLL = null // allow GC + res + } + + def #::[B >: A](elem: => B): NonEmptyLazyList[B] = + create(elem #:: ll) + def #:::[B >: A](prefix: => NonEmptyLazyList[B]): NonEmptyLazyList[B] = + create(prefix.toLazyList #::: ll) + def #:::[B >: A](prefix: => LazyList[B]): Maybe[B] = + new Maybe(() => prefix #::: ll) + } + + final class Deferrer[A] private[NonEmptyLazyList] (private val nell: () => NonEmptyLazyList[A]) extends AnyVal { + def #::[B >: A](elem: => B): NonEmptyLazyList[B] = + create(elem #:: nell().toLazyList) + def #:::[B >: A](prefix: => NonEmptyLazyList[B]): NonEmptyLazyList[B] = + create(prefix.toLazyList #::: nell().toLazyList) + def #:::[B >: A](prefix: => LazyList[B]): NonEmptyLazyList[B] = + create(prefix #::: nell().toLazyList) + } + + implicit def toDeferrer[A](nell: => NonEmptyLazyList[A]): Deferrer[A] = + new Deferrer(() => nell) + implicit def catsNonEmptyLazyListOps[A](value: NonEmptyLazyList[A]): NonEmptyLazyListOps[A] = new NonEmptyLazyListOps(value) } @@ -118,11 +150,7 @@ class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A]) final def +:[AA >: A](a: AA): NonEmptyLazyList[AA] = prepend(a) - /** - * Alias for [[prepend]]. - */ - // TODO: `a` should be by-name and this method should not be listed as an - // alias for `prepend`, but it's too late to change that in this version + @deprecated("use Deferrer construction instead") final def #::[AA >: A](a: AA): NonEmptyLazyList[AA] = prepend(a) @@ -139,52 +167,76 @@ class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A]) append(a) /** - * concatenates this with `ll` + * Concatenates this with `ll`; equivalent to `appendLazyList` + */ + final def concat[AA >: A](ll: => LazyList[AA]): NonEmptyLazyList[AA] = + appendLazyList(ll) + + /** + * Alias for `concat` */ - final def concat[AA >: A](ll: LazyList[AA]): NonEmptyLazyList[AA] = - create(toLazyList ++ ll) + final def ++[AA >: A](ll: => LazyList[AA]): NonEmptyLazyList[AA] = + concat(ll) /** - * Concatenates this with `nell` + * Concatenates this with `nell`; equivalent to `appendNell` */ - final def concatNell[AA >: A](nell: NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = - create(toLazyList ++ nell.toLazyList) + final def concatNell[AA >: A](nell: => NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = + appendNell(nell) /** - * Alias for concatNell + * Alias for `concatNell` */ - final def ++[AA >: A](nell: NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = + final def ++[AA >: A](nell: => NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = concatNell(nell) /** * Appends the given LazyList */ - final def appendLazyList[AA >: A](nell: LazyList[AA]): NonEmptyLazyList[AA] = - create(toLazyList ++ nell) + final def appendLazyList[AA >: A](ll: => LazyList[AA]): NonEmptyLazyList[AA] = + create(toLazyList #::: ll) /** * Alias for `appendLazyList` */ - final def :++[AA >: A](c: LazyList[AA]): NonEmptyLazyList[AA] = - appendLazyList(c) + final def :++[AA >: A](ll: => LazyList[AA]): NonEmptyLazyList[AA] = + appendLazyList(ll) + + /** + * Prepends the given NonEmptyLazyList + */ + final def appendNell[AA >: A](nell: => NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = + create(toLazyList #::: nell.toLazyList) + + /** + * Alias for `appendNell` + */ + final def :++[AA >: A](nell: => NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = + appendNell(nell) /** * Prepends the given LazyList */ - final def prependLazyList[AA >: A](c: LazyList[AA]): NonEmptyLazyList[AA] = - create(c ++ toLazyList) + final def prependLazyList[AA >: A](ll: => LazyList[AA]): NonEmptyLazyList[AA] = + create(ll #::: toLazyList) + + /** + * Alias for `prependLazyList` + */ + final def ++:[AA >: A](ll: => LazyList[AA]): NonEmptyLazyList[AA] = + prependLazyList(ll) /** * Prepends the given NonEmptyLazyList */ - final def prependNell[AA >: A](c: NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = - create(c.toLazyList ++ toLazyList) + final def prependNell[AA >: A](nell: => NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = + create(nell.toLazyList #::: toLazyList) /** * Alias for `prependNell` */ - final def ++:[AA >: A](c: NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = - prependNell(c) + final def ++:[AA >: A](nell: => NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = + prependNell(nell) /** * Converts this NonEmptyLazyList to a `NonEmptyList`. From 3c5e56437475b765b29628754ae561cc676dd837 Mon Sep 17 00:00:00 2001 From: Marissa Date: Sun, 3 Sep 2023 14:14:27 -0400 Subject: [PATCH 03/12] fixup! Redesign `NonEmptyLazyList` to be maximally lazy DummyImplicit --- .../scala-2.13+/cats/data/NonEmptyLazyList.scala | 14 +++++++------- 1 file changed, 7 insertions(+), 7 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 c12e389539..ca4eb25bc3 100644 --- a/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala +++ b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala @@ -85,19 +85,19 @@ object NonEmptyLazyList extends NonEmptyLazyListInstances { def #::[B >: A](elem: => B): NonEmptyLazyList[B] = create(elem #:: ll) - def #:::[B >: A](prefix: => NonEmptyLazyList[B]): NonEmptyLazyList[B] = - create(prefix.toLazyList #::: ll) def #:::[B >: A](prefix: => LazyList[B]): Maybe[B] = new Maybe(() => prefix #::: ll) + def #:::[B >: A](prefix: => NonEmptyLazyList[B])(implicit d: DummyImplicit): NonEmptyLazyList[B] = + create(prefix.toLazyList #::: ll) } final class Deferrer[A] private[NonEmptyLazyList] (private val nell: () => NonEmptyLazyList[A]) extends AnyVal { def #::[B >: A](elem: => B): NonEmptyLazyList[B] = create(elem #:: nell().toLazyList) - def #:::[B >: A](prefix: => NonEmptyLazyList[B]): NonEmptyLazyList[B] = - create(prefix.toLazyList #::: nell().toLazyList) def #:::[B >: A](prefix: => LazyList[B]): NonEmptyLazyList[B] = create(prefix #::: nell().toLazyList) + def #:::[B >: A](prefix: => NonEmptyLazyList[B])(implicit d: DummyImplicit): NonEmptyLazyList[B] = + create(prefix.toLazyList #::: nell().toLazyList) } implicit def toDeferrer[A](nell: => NonEmptyLazyList[A]): Deferrer[A] = @@ -187,7 +187,7 @@ class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A]) /** * Alias for `concatNell` */ - final def ++[AA >: A](nell: => NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = + final def ++[AA >: A](nell: => NonEmptyLazyList[AA])(implicit d: DummyImplicit): NonEmptyLazyList[AA] = concatNell(nell) /** @@ -211,7 +211,7 @@ class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A]) /** * Alias for `appendNell` */ - final def :++[AA >: A](nell: => NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = + final def :++[AA >: A](nell: => NonEmptyLazyList[AA])(implicit d: DummyImplicit): NonEmptyLazyList[AA] = appendNell(nell) /** @@ -235,7 +235,7 @@ class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A]) /** * Alias for `prependNell` */ - final def ++:[AA >: A](nell: => NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = + final def ++:[AA >: A](nell: => NonEmptyLazyList[AA])(implicit d: DummyImplicit): NonEmptyLazyList[AA] = prependNell(nell) /** From d80d9596a17c5b1c84049b11fb862bac91c9a431 Mon Sep 17 00:00:00 2001 From: Marissa Date: Sun, 3 Sep 2023 19:59:19 -0400 Subject: [PATCH 04/12] fixup! fixup! Redesign `NonEmptyLazyList` to be maximally lazy no more private var --- .../src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala | 8 ++------ 1 file changed, 2 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 ca4eb25bc3..79eb61e0bd 100644 --- a/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala +++ b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala @@ -74,14 +74,10 @@ object NonEmptyLazyList extends NonEmptyLazyListInstances { // allows the creation of fully lazy `NonEmptyLazyList`s by prepending to this def maybe[A](ll: => LazyList[A]): Maybe[A] = new Maybe(() => ll) - final class Maybe[A] private[NonEmptyLazyList] (private[this] var mkLL: () => LazyList[A]) { + final class Maybe[A] private[NonEmptyLazyList] (mkLL: () => LazyList[A]) { // because instances of this class are created explicitly, they might be // reused, and we don't want to re-evaluate `mkLL` - private[this] lazy val ll = { - val res = mkLL() - mkLL = null // allow GC - res - } + private[this] lazy val ll = mkLL() def #::[B >: A](elem: => B): NonEmptyLazyList[B] = create(elem #:: ll) From e948a1d39fcfcdf1b95a68285d33c48f91746ceb Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Mon, 4 Sep 2023 03:45:21 +0000 Subject: [PATCH 05/12] Restore bincompat --- .../cats/data/NonEmptyLazyList.scala | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) 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 79eb61e0bd..3c9fcc61e5 100644 --- a/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala +++ b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala @@ -65,9 +65,17 @@ object NonEmptyLazyList extends NonEmptyLazyListInstances { def fromLazyListPrepend[A](a: => A, ll: => LazyList[A]): NonEmptyLazyList[A] = create(a #:: ll) + @deprecated("Use overload with by-name args", "2.11.0") + def fromLazyListPrepend[A]()(a: A, ll: LazyList[A]): NonEmptyLazyList[A] = + fromLazyListPrepend(a, ll) + def fromLazyListAppend[A](ll: => LazyList[A], a: => A): NonEmptyLazyList[A] = create(ll #::: a #:: LazyList.empty) + @deprecated("Use overload with by-name args", "2.11.0") + def fromLazyListAppend[A]()(ll: LazyList[A], a: A): NonEmptyLazyList[A] = + fromLazyListAppend(ll, a) + def apply[A](a: => A, as: A*): NonEmptyLazyList[A] = create(a #:: LazyList.from(as)) @@ -168,6 +176,10 @@ class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A]) final def concat[AA >: A](ll: => LazyList[AA]): NonEmptyLazyList[AA] = appendLazyList(ll) + @deprecated("Use overload with by-name args", "2.11.0") + final private[data] def concat[AA >: A]()(ll: LazyList[AA]): NonEmptyLazyList[AA] = + concat(ll) + /** * Alias for `concat` */ @@ -180,24 +192,40 @@ class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A]) final def concatNell[AA >: A](nell: => NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = appendNell(nell) + @deprecated("Use overload with by-name args", "2.11.0") + final private[data] def concatNell[AA >: A]()(nell: NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = + concatNell(nell) + /** * Alias for `concatNell` */ final def ++[AA >: A](nell: => NonEmptyLazyList[AA])(implicit d: DummyImplicit): NonEmptyLazyList[AA] = concatNell(nell) + @deprecated("Use overload with by-name args", "2.11.0") + final private[data] def ++[AA >: A]()(ll: NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = + ++(ll) + /** * Appends the given LazyList */ final def appendLazyList[AA >: A](ll: => LazyList[AA]): NonEmptyLazyList[AA] = create(toLazyList #::: ll) + @deprecated("Use overload with by-name args", "2.11.0") + final private[data] def appendLazyList[AA >: A]()(ll: LazyList[AA]): NonEmptyLazyList[AA] = + appendLazyList(ll) + /** * Alias for `appendLazyList` */ final def :++[AA >: A](ll: => LazyList[AA]): NonEmptyLazyList[AA] = appendLazyList(ll) + @deprecated("Use overload with by-name args", "2.11.0") + final private[data] def :++[AA >: A]()(ll: LazyList[AA]): NonEmptyLazyList[AA] = + appendLazyList(ll) + /** * Prepends the given NonEmptyLazyList */ @@ -210,12 +238,20 @@ class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A]) final def :++[AA >: A](nell: => NonEmptyLazyList[AA])(implicit d: DummyImplicit): NonEmptyLazyList[AA] = appendNell(nell) + @deprecated("Use overload with by-name args", "2.11.0") + final private[data] def ++:[AA >: A]()(ll: NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = + ++:(ll) + /** * Prepends the given LazyList */ final def prependLazyList[AA >: A](ll: => LazyList[AA]): NonEmptyLazyList[AA] = create(ll #::: toLazyList) + @deprecated("Use overload with by-name args", "2.11.0") + final private[data] def prependLazyList[AA >: A]()(ll: LazyList[AA]): NonEmptyLazyList[AA] = + prependLazyList(ll) + /** * Alias for `prependLazyList` */ @@ -228,6 +264,10 @@ class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A]) final def prependNell[AA >: A](nell: => NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = create(nell.toLazyList #::: toLazyList) + @deprecated("Use overload with by-name args", "2.11.0") + final private[data] def prependNell[AA >: A]()(nell: NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = + prependNell(nell) + /** * Alias for `prependNell` */ From 10f8c21c84988b56af8d71291235d8775f7eb873 Mon Sep 17 00:00:00 2001 From: Marissa Date: Mon, 4 Sep 2023 00:15:38 -0400 Subject: [PATCH 06/12] Stop old `#::` from being called --- .../scala-2.13+/cats/data/NonEmptyLazyList.scala | 14 +++++++------- 1 file changed, 7 insertions(+), 7 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 3c9fcc61e5..580cd396db 100644 --- a/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala +++ b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala @@ -87,20 +87,20 @@ object NonEmptyLazyList extends NonEmptyLazyListInstances { // reused, and we don't want to re-evaluate `mkLL` private[this] lazy val ll = mkLL() - def #::[B >: A](elem: => B): NonEmptyLazyList[B] = + def #::[AA >: A](elem: => AA): NonEmptyLazyList[AA] = create(elem #:: ll) - def #:::[B >: A](prefix: => LazyList[B]): Maybe[B] = + def #:::[AA >: A](prefix: => LazyList[AA]): Maybe[AA] = new Maybe(() => prefix #::: ll) - def #:::[B >: A](prefix: => NonEmptyLazyList[B])(implicit d: DummyImplicit): NonEmptyLazyList[B] = + def #:::[AA >: A](prefix: => NonEmptyLazyList[AA])(implicit d: DummyImplicit): NonEmptyLazyList[AA] = create(prefix.toLazyList #::: ll) } final class Deferrer[A] private[NonEmptyLazyList] (private val nell: () => NonEmptyLazyList[A]) extends AnyVal { - def #::[B >: A](elem: => B): NonEmptyLazyList[B] = + def #::[AA >: A](elem: => AA): NonEmptyLazyList[AA] = create(elem #:: nell().toLazyList) - def #:::[B >: A](prefix: => LazyList[B]): NonEmptyLazyList[B] = + def #:::[AA >: A](prefix: => LazyList[AA]): NonEmptyLazyList[AA] = create(prefix #::: nell().toLazyList) - def #:::[B >: A](prefix: => NonEmptyLazyList[B])(implicit d: DummyImplicit): NonEmptyLazyList[B] = + def #:::[AA >: A](prefix: => NonEmptyLazyList[AA])(implicit d: DummyImplicit): NonEmptyLazyList[AA] = create(prefix.toLazyList #::: nell().toLazyList) } @@ -155,7 +155,7 @@ class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A]) prepend(a) @deprecated("use Deferrer construction instead") - final def #::[AA >: A](a: AA): NonEmptyLazyList[AA] = + final def #::[AA >: A]()(a: AA): NonEmptyLazyList[AA] = prepend(a) /** From 4f842eb18b8d681a450d06a5cd079f9a745e80ca Mon Sep 17 00:00:00 2001 From: Marissa Date: Sat, 9 Sep 2023 16:47:38 -0400 Subject: [PATCH 07/12] fixup! Stop old `#::` from being called better fromLazyListUnsafe --- .../src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 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 580cd396db..da35362514 100644 --- a/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala +++ b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala @@ -49,9 +49,11 @@ object NonEmptyLazyList extends NonEmptyLazyListInstances { def fromLazyList[A](as: LazyList[A]): Option[NonEmptyLazyList[A]] = if (as.nonEmpty) Some(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 fromLazyListUnsafe[A](ll: LazyList[A]): NonEmptyLazyList[A] = { + @inline def ex = new IllegalArgumentException("Cannot create NonEmptyLazyList from empty LazyList") + if (ll.knownSize == 0) throw ex + else create({ if (ll.isEmpty) throw ex else ll } #::: LazyList.empty) + } def fromNonEmptyList[A](as: NonEmptyList[A]): NonEmptyLazyList[A] = create(LazyList.from(as.toList)) From df8778ea99114b3170d994d5cb2dbb327be8f64a Mon Sep 17 00:00:00 2001 From: Marissa Date: Sat, 9 Sep 2023 16:52:21 -0400 Subject: [PATCH 08/12] fixup! fixup! Stop old `#::` from being called docs --- .../cats/data/NonEmptyLazyList.scala | 21 ++++++++++++++++++- 1 file changed, 20 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 da35362514..163d15cd4f 100644 --- a/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala +++ b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala @@ -81,27 +81,46 @@ object NonEmptyLazyList extends NonEmptyLazyListInstances { def apply[A](a: => A, as: A*): NonEmptyLazyList[A] = create(a #:: LazyList.from(as)) - // allows the creation of fully lazy `NonEmptyLazyList`s by prepending to this + /** + * Wraps a `LazyList` that may or may not be empty so that individual + * elements or `NonEmptyLazyList`s can be prepended to it to construct a + * result that is guaranteed not to be empty without evaluating any elements. + */ def maybe[A](ll: => LazyList[A]): Maybe[A] = new Maybe(() => ll) + /** + * Wrapper around a `LazyList` that may or may not be empty so that individual + * elements or `NonEmptyLazyList`s can be prepended to it to construct a + * result that is guaranteed not to be empty without evaluating any elements. + */ final class Maybe[A] private[NonEmptyLazyList] (mkLL: () => LazyList[A]) { // because instances of this class are created explicitly, they might be // reused, and we don't want to re-evaluate `mkLL` private[this] lazy val ll = mkLL() + /** Prepends a single element, yielding a `NonEmptyLazyList`. */ def #::[AA >: A](elem: => AA): NonEmptyLazyList[AA] = create(elem #:: ll) + + /** Prepends a `LazyList`, yielding another [[Maybe]]. */ def #:::[AA >: A](prefix: => LazyList[AA]): Maybe[AA] = new Maybe(() => prefix #::: ll) + + /** Prepends a `NonEmptyLazyList`, yielding a `NonEmptyLazyList`. */ def #:::[AA >: A](prefix: => NonEmptyLazyList[AA])(implicit d: DummyImplicit): NonEmptyLazyList[AA] = create(prefix.toLazyList #::: ll) } final class Deferrer[A] private[NonEmptyLazyList] (private val nell: () => NonEmptyLazyList[A]) extends AnyVal { + /** Prepends a single element. */ def #::[AA >: A](elem: => AA): NonEmptyLazyList[AA] = create(elem #:: nell().toLazyList) + + /** Prepends a `LazyList`. */ def #:::[AA >: A](prefix: => LazyList[AA]): NonEmptyLazyList[AA] = create(prefix #::: nell().toLazyList) + + /** Prepends a `NonEmptyLazyList`. */ def #:::[AA >: A](prefix: => NonEmptyLazyList[AA])(implicit d: DummyImplicit): NonEmptyLazyList[AA] = create(prefix.toLazyList #::: nell().toLazyList) } From 5e6b46e8627ee7a97a0dee62ed93fb159b28878c Mon Sep 17 00:00:00 2001 From: Marissa Date: Sat, 9 Sep 2023 17:32:09 -0400 Subject: [PATCH 09/12] fixup! fixup! fixup! Stop old `#::` from being called formatting --- core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala | 1 + 1 file changed, 1 insertion(+) 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 163d15cd4f..f43c4e3646 100644 --- a/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala +++ b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala @@ -112,6 +112,7 @@ object NonEmptyLazyList extends NonEmptyLazyListInstances { } final class Deferrer[A] private[NonEmptyLazyList] (private val nell: () => NonEmptyLazyList[A]) extends AnyVal { + /** Prepends a single element. */ def #::[AA >: A](elem: => AA): NonEmptyLazyList[AA] = create(elem #:: nell().toLazyList) From 29d095b20f67f1ae68a2fff950237b3d59d28831 Mon Sep 17 00:00:00 2001 From: Marissa Date: Sat, 9 Sep 2023 22:33:54 -0400 Subject: [PATCH 10/12] fixup! fixup! fixup! fixup! Stop old `#::` from being called laziness fixes, oops --- .../cats/data/NonEmptyLazyList.scala | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 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 f43c4e3646..d7bedd6dc6 100644 --- a/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala +++ b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala @@ -52,7 +52,7 @@ object NonEmptyLazyList extends NonEmptyLazyListInstances { def fromLazyListUnsafe[A](ll: LazyList[A]): NonEmptyLazyList[A] = { @inline def ex = new IllegalArgumentException("Cannot create NonEmptyLazyList from empty LazyList") if (ll.knownSize == 0) throw ex - else create({ if (ll.isEmpty) throw ex else ll } #::: LazyList.empty) + else create(LazyList.empty #::: { if (ll.isEmpty) throw ex else ll }) } def fromNonEmptyList[A](as: NonEmptyList[A]): NonEmptyLazyList[A] = @@ -72,7 +72,7 @@ object NonEmptyLazyList extends NonEmptyLazyListInstances { fromLazyListPrepend(a, ll) def fromLazyListAppend[A](ll: => LazyList[A], a: => A): NonEmptyLazyList[A] = - create(ll #::: a #:: LazyList.empty) + create(LazyList.empty #::: ll #::: a #:: LazyList.empty) @deprecated("Use overload with by-name args", "2.11.0") def fromLazyListAppend[A]()(ll: LazyList[A], a: A): NonEmptyLazyList[A] = @@ -86,17 +86,14 @@ object NonEmptyLazyList extends NonEmptyLazyListInstances { * elements or `NonEmptyLazyList`s can be prepended to it to construct a * result that is guaranteed not to be empty without evaluating any elements. */ - def maybe[A](ll: => LazyList[A]): Maybe[A] = new Maybe(() => ll) + def maybe[A](ll: => LazyList[A]): Maybe[A] = new Maybe(LazyList.empty #::: ll) /** * Wrapper around a `LazyList` that may or may not be empty so that individual * elements or `NonEmptyLazyList`s can be prepended to it to construct a * result that is guaranteed not to be empty without evaluating any elements. */ - final class Maybe[A] private[NonEmptyLazyList] (mkLL: () => LazyList[A]) { - // because instances of this class are created explicitly, they might be - // reused, and we don't want to re-evaluate `mkLL` - private[this] lazy val ll = mkLL() + final class Maybe[A] private[NonEmptyLazyList] (private val ll: LazyList[A]) extends AnyVal { /** Prepends a single element, yielding a `NonEmptyLazyList`. */ def #::[AA >: A](elem: => AA): NonEmptyLazyList[AA] = @@ -104,11 +101,11 @@ object NonEmptyLazyList extends NonEmptyLazyListInstances { /** Prepends a `LazyList`, yielding another [[Maybe]]. */ def #:::[AA >: A](prefix: => LazyList[AA]): Maybe[AA] = - new Maybe(() => prefix #::: ll) + new Maybe(LazyList.empty #::: prefix #::: ll) /** Prepends a `NonEmptyLazyList`, yielding a `NonEmptyLazyList`. */ def #:::[AA >: A](prefix: => NonEmptyLazyList[AA])(implicit d: DummyImplicit): NonEmptyLazyList[AA] = - create(prefix.toLazyList #::: ll) + create(LazyList.empty #::: prefix.toLazyList #::: ll) } final class Deferrer[A] private[NonEmptyLazyList] (private val nell: () => NonEmptyLazyList[A]) extends AnyVal { @@ -119,11 +116,11 @@ object NonEmptyLazyList extends NonEmptyLazyListInstances { /** Prepends a `LazyList`. */ def #:::[AA >: A](prefix: => LazyList[AA]): NonEmptyLazyList[AA] = - create(prefix #::: nell().toLazyList) + create(LazyList.empty #::: prefix #::: nell().toLazyList) /** Prepends a `NonEmptyLazyList`. */ def #:::[AA >: A](prefix: => NonEmptyLazyList[AA])(implicit d: DummyImplicit): NonEmptyLazyList[AA] = - create(prefix.toLazyList #::: nell().toLazyList) + create(LazyList.empty #::: prefix.toLazyList #::: nell().toLazyList) } implicit def toDeferrer[A](nell: => NonEmptyLazyList[A]): Deferrer[A] = @@ -268,7 +265,7 @@ class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A]) * Prepends the given LazyList */ final def prependLazyList[AA >: A](ll: => LazyList[AA]): NonEmptyLazyList[AA] = - create(ll #::: toLazyList) + create(LazyList.empty #::: ll #::: toLazyList) @deprecated("Use overload with by-name args", "2.11.0") final private[data] def prependLazyList[AA >: A]()(ll: LazyList[AA]): NonEmptyLazyList[AA] = @@ -284,7 +281,7 @@ class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A]) * Prepends the given NonEmptyLazyList */ final def prependNell[AA >: A](nell: => NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = - create(nell.toLazyList #::: toLazyList) + create(LazyList.empty #::: nell.toLazyList #::: toLazyList) @deprecated("Use overload with by-name args", "2.11.0") final private[data] def prependNell[AA >: A]()(nell: NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = From 46754d4830b81ee8e6b636f53fbda2f074312640 Mon Sep 17 00:00:00 2001 From: Marissa Date: Mon, 18 Sep 2023 07:37:07 -0400 Subject: [PATCH 11/12] fixup! fixup! fixup! fixup! fixup! Stop old `#::` from being called improve API, renaming Maybe to Tail --- .../cats/data/NonEmptyLazyList.scala | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 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 d7bedd6dc6..3b5096b7d2 100644 --- a/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala +++ b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala @@ -82,27 +82,30 @@ object NonEmptyLazyList extends NonEmptyLazyListInstances { create(a #:: LazyList.from(as)) /** - * Wraps a `LazyList` that may or may not be empty so that individual - * elements or `NonEmptyLazyList`s can be prepended to it to construct a - * result that is guaranteed not to be empty without evaluating any elements. + * Wraps a `LazyList` to be used as the tail of a `NonEmptyLazyList`, + * so that individual elements or `NonEmptyLazyList`s can be prepended to it + * to construct a non-empty result without evaluating any elements. */ - def maybe[A](ll: => LazyList[A]): Maybe[A] = new Maybe(LazyList.empty #::: ll) + def tail[A](ll: => LazyList[A]): Tail[A] = new Tail(LazyList.empty #::: ll) /** - * Wrapper around a `LazyList` that may or may not be empty so that individual - * elements or `NonEmptyLazyList`s can be prepended to it to construct a - * result that is guaranteed not to be empty without evaluating any elements. + * A wrapped empty `LazyList` to be used as the tail of a `NonEmptyLazyList`, + * so that individual elements or `NonEmptyLazyList`s can be prepended to it + * to construct a non-empty result without evaluating any elements. */ - final class Maybe[A] private[NonEmptyLazyList] (private val ll: LazyList[A]) extends AnyVal { + def emptyTail[A]: Tail[A] = new Tail(LazyList.empty) + + /** + * Wrapper around a `LazyList` to be used as the tail of a `NonEmptyLazyList`, + * so that individual elements or `NonEmptyLazyList`s can be prepended to it + * to construct a non-empty result without evaluating any elements. + */ + final class Tail[A] private[NonEmptyLazyList] (private val ll: LazyList[A]) extends AnyVal { /** Prepends a single element, yielding a `NonEmptyLazyList`. */ def #::[AA >: A](elem: => AA): NonEmptyLazyList[AA] = create(elem #:: ll) - /** Prepends a `LazyList`, yielding another [[Maybe]]. */ - def #:::[AA >: A](prefix: => LazyList[AA]): Maybe[AA] = - new Maybe(LazyList.empty #::: prefix #::: ll) - /** Prepends a `NonEmptyLazyList`, yielding a `NonEmptyLazyList`. */ def #:::[AA >: A](prefix: => NonEmptyLazyList[AA])(implicit d: DummyImplicit): NonEmptyLazyList[AA] = create(LazyList.empty #::: prefix.toLazyList #::: ll) From 2bb52cf977e1baf9366b56c39354aaf194635054 Mon Sep 17 00:00:00 2001 From: Marissa Date: Mon, 18 Sep 2023 07:55:56 -0400 Subject: [PATCH 12/12] fixup! fixup! fixup! fixup! fixup! fixup! Stop old `#::` from being called examples and fix mistake --- .../scala-2.13+/cats/data/NonEmptyLazyList.scala | 12 +++++++++++- 1 file changed, 11 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 3b5096b7d2..4d729bfc2e 100644 --- a/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala +++ b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala @@ -85,6 +85,11 @@ object NonEmptyLazyList extends NonEmptyLazyListInstances { * Wraps a `LazyList` to be used as the tail of a `NonEmptyLazyList`, * so that individual elements or `NonEmptyLazyList`s can be prepended to it * to construct a non-empty result without evaluating any elements. + * + * @example + * {{{ + * val nell: NonEmptyLazyList[Int] = 4 #:: NonEmptyLazyList.tail(LazyList.from(5)) + * }}} */ def tail[A](ll: => LazyList[A]): Tail[A] = new Tail(LazyList.empty #::: ll) @@ -92,6 +97,11 @@ object NonEmptyLazyList extends NonEmptyLazyListInstances { * A wrapped empty `LazyList` to be used as the tail of a `NonEmptyLazyList`, * so that individual elements or `NonEmptyLazyList`s can be prepended to it * to construct a non-empty result without evaluating any elements. + * + * @example + * {{{ + * val nell: NonEmptyLazyList[Int] = 4 #:: NonEmptyLazyList.emptyTail + * }}} */ def emptyTail[A]: Tail[A] = new Tail(LazyList.empty) @@ -107,7 +117,7 @@ object NonEmptyLazyList extends NonEmptyLazyListInstances { create(elem #:: ll) /** Prepends a `NonEmptyLazyList`, yielding a `NonEmptyLazyList`. */ - def #:::[AA >: A](prefix: => NonEmptyLazyList[AA])(implicit d: DummyImplicit): NonEmptyLazyList[AA] = + def #:::[AA >: A](prefix: => NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = create(LazyList.empty #::: prefix.toLazyList #::: ll) }