diff --git a/core/src/main/scala/cats/data/NonEmptyList.scala b/core/src/main/scala-2.12-/cats/data/NonEmptyList.scala similarity index 100% rename from core/src/main/scala/cats/data/NonEmptyList.scala rename to core/src/main/scala-2.12-/cats/data/NonEmptyList.scala diff --git a/core/src/main/scala/cats/data/NonEmptyVector.scala b/core/src/main/scala-2.12-/cats/data/NonEmptyVector.scala similarity index 98% rename from core/src/main/scala/cats/data/NonEmptyVector.scala rename to core/src/main/scala-2.12-/cats/data/NonEmptyVector.scala index eda86274f5..568750a91c 100644 --- a/core/src/main/scala/cats/data/NonEmptyVector.scala +++ b/core/src/main/scala-2.12-/cats/data/NonEmptyVector.scala @@ -5,7 +5,6 @@ import cats.data.NonEmptyVector.ZipNonEmptyVector import scala.annotation.tailrec import scala.collection.immutable.{TreeSet, VectorBuilder} import cats.instances.vector._ -import kernel.compat.scalaVersionSpecific._ /** * A data type which represents a `Vector` guaranteed to contain at least one element. @@ -219,7 +218,7 @@ final class NonEmptyVector[+A] private (val toVector: Vector[A]) extends AnyVal * }}} */ def zipWith[B, C](b: NonEmptyVector[B])(f: (A, B) => C): NonEmptyVector[C] = - NonEmptyVector.fromVectorUnsafe(toVector.lazyZip(b.toVector).map(f)) + NonEmptyVector.fromVectorUnsafe((toVector, b.toVector).zipped.map(f)) def reverse: NonEmptyVector[A] = new NonEmptyVector(toVector.reverse) @@ -234,7 +233,6 @@ final class NonEmptyVector[+A] private (val toVector: Vector[A]) extends AnyVal new NonEmptyVector(toVector.sorted(AA.toOrdering)) } -@suppressUnusedImportWarningForScalaVersionSpecific sealed abstract private[data] class NonEmptyVectorInstances { implicit val catsDataInstancesForNonEmptyVector: SemigroupK[NonEmptyVector] diff --git a/core/src/main/scala-2.12-/cats/data/ScalaVersionSpecificPackage.scala b/core/src/main/scala-2.12-/cats/data/ScalaVersionSpecificPackage.scala new file mode 100644 index 0000000000..24a811e068 --- /dev/null +++ b/core/src/main/scala-2.12-/cats/data/ScalaVersionSpecificPackage.scala @@ -0,0 +1,5 @@ +package cats + +package data + +abstract private[data] class ScalaVersionSpecificPackage 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..97f4cb09bb --- /dev/null +++ b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala @@ -0,0 +1,379 @@ +package cats +package data + +import NonEmptyLazyList.create +import kernel.PartialOrder +import instances.lazyList._ + +import scala.collection.immutable.TreeSet + +object NonEmptyLazyList extends NonEmptyLazyListInstances { + 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 { + + /** + * Converts this NonEmptyLazyList to a `LazyList` + */ + final def toLazyList: LazyList[A] = NonEmptyLazyList.unwrap(value) + + final def map[B](f: A => B): NonEmptyLazyList[B] = create(toLazyList.map(f)) + + /** + * Returns the last element + */ + final def last: A = toLazyList.last + + /** + * Returns all elements but the last + */ + final def init: LazyList[A] = toLazyList.init + + /** + * Returns the size of this NonEmptyLazyList + */ + final def size: Int = toLazyList.size + + /** + * Returns the length of this NonEmptyLazyList + */ + final def length: Int = toLazyList.length + + /** + * Returns a new NonEmptyLazyList consisting of `a` followed by this + */ + final def prepend[AA >: A](a: AA): NonEmptyLazyList[AA] = + create(a #:: toLazyList) + + /** + * Alias for [[prepend]]. + */ + final def +:[AA >: A](a: AA): NonEmptyLazyList[AA] = + prepend(a) + + /** + * Alias for [[prepend]]. + */ + final def #::[AA >: A](a: AA): NonEmptyLazyList[AA] = + prepend(a) + + /** + * Returns a new NonEmptyLazyList consisting of this followed by `a` + */ + final def append[AA >: A](a: AA): NonEmptyLazyList[AA] = + create(toLazyList :+ a) + + /** + * Alias for [[append]]. + */ + final def :+[AA >: A](a: AA): NonEmptyLazyList[AA] = + append(a) + + /** + * concatenates this with `ll` + */ + final def concat[AA >: A](ll: LazyList[AA]): NonEmptyLazyList[AA] = + create(toLazyList ++ ll) + + /** + * Concatenates this with `nell` + */ + final def concatNell[AA >: A](nell: NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = + create(toLazyList ++ nell.toLazyList) + + /** + * Alias for concatNell + */ + 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] = + if (nell.isEmpty) value + else create(toLazyList ++ nell) + + /** + * Alias for `appendLazyList` + */ + final def :++[AA >: A](c: LazyList[AA]): NonEmptyLazyList[AA] = + appendLazyList(c) + + /** + * Prepends the given LazyList + */ + final def prependLazyList[AA >: A](c: LazyList[AA]): NonEmptyLazyList[AA] = + if (c.isEmpty) value + else create(c ++ toLazyList) + + /** + * Prepends the given NonEmptyLazyList + */ + final def prependNell[AA >: A](c: NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = + create(c.toLazyList ++ toLazyList) + + /** + * Alias for `prependNell` + */ + final def ++:[AA >: A](c: NonEmptyLazyList[AA]): NonEmptyLazyList[AA] = + prependNell(c) + + /** + * Converts this NonEmptyLazyList to a `NonEmptyList`. + */ // TODO also add toNonEmptyLazyList to NonEmptyList? + final def toNonEmptyList: NonEmptyList[A] = + NonEmptyList.fromListUnsafe(toLazyList.toList) + + /** + * Converts this LazyList to a `NonEmptyVector`. + */ + final def toNonEmptyVector: NonEmptyVector[A] = + NonEmptyVector.fromVectorUnsafe(toLazyList.toVector) + + /** + * Returns the first element + */ + final def head: A = toLazyList.head + + /** + * Returns all but the first element + */ + final def tail: LazyList[A] = toLazyList.tail + + /** + * Tests if some element is contained in this NonEmptyLazyList + */ + final def contains(a: A)(implicit A: Eq[A]): Boolean = + toLazyList.contains(a) + + /** + * Tests whether a predicate holds for all elements + */ + final def forall(p: A => Boolean): Boolean = + toLazyList.forall(p) + + /** + * Tests whether a predicate holds for at least one element of this LazyList + */ + final def exists(f: A => Boolean): Boolean = + toLazyList.exists(f) + + /** + * Returns the first value that matches the given predicate. + */ + final def find(f: A => Boolean): Option[A] = + toLazyList.find(f) + + /** + * 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) + + /** + * 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) + + /** + * Filters all elements of this NonEmptyLazyList that do not satisfy the given predicate. + */ + final def filter(p: A => Boolean): LazyList[A] = toLazyList.filter(p) + + /** + * Filters all elements of this NonEmptyLazyList that satisfy the given predicate. + */ + final def filterNot(p: A => Boolean): LazyList[A] = filter(t => !p(t)) + + /** + * Left-associative fold using f. + */ + final def foldLeft[B](b: B)(f: (B, A) => B): B = + toLazyList.foldLeft(b)(f) + + /** + * Right-associative fold using f. + */ + final def foldRight[B](z: B)(f: (A, B) => B): B = + toLazyList.foldRight(z)(f) + + /** + * Left-associative reduce using f. + */ + final def reduceLeft(f: (A, A) => A): A = + toLazyList.reduceLeft(f) + + /** + * Apply `f` to the "initial element" of this LazyList and lazily combine it + * with every other value using the given function `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 + } + + /** + * Right-associative reduce using f. + */ + final def reduceRight[AA >: A](f: (A, AA) => AA): AA = + toLazyList.reduceRight(f) + + /** + * Apply `f` to the "initial element" of this NonEmptyLazyList and + * lazily combine it with every other value using the given function `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 + */ + final def reduce[AA >: A](implicit S: Semigroup[AA]): AA = + S.combineAllOption(iterator).get + + /** + * Applies the supplied function to each element and returns a new NonEmptyLazyList from the concatenated results + */ + final def flatMap[B](f: A => NonEmptyLazyList[B]): NonEmptyLazyList[B] = + create(toLazyList.flatMap(f.andThen(_.toLazyList))) + + /** + * Zips this `NonEmptyLazyList` with another `NonEmptyLazyList` and applies a function for each pair of elements + */ + 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) }) + + /** + * Zips each element of this `NonEmptyLazyList` with its index + */ + final def zipWithIndex: NonEmptyLazyList[(A, Int)] = + create(toLazyList.zipWithIndex) + + final def iterator: Iterator[A] = toLazyList.iterator + + final def reverseIterator: Iterator[A] = toLazyList.reverseIterator + + /** + * Reverses this `NonEmptyLazyList` + */ + final def reverse: NonEmptyLazyList[A] = + create(toLazyList.reverse) + + /** + * Remove duplicates. Duplicates are checked using `Order[_]` instance. + */ + def distinct[AA >: A](implicit O: Order[AA]): NonEmptyLazyList[AA] = { + implicit val ord = O.toOrdering + + val buf = LazyList.newBuilder[AA] + toLazyList.foldLeft(TreeSet.empty[AA]) { (elementsSoFar, a) => + if (elementsSoFar(a)) elementsSoFar + else { + buf += a; elementsSoFar + a + } + } + + create(buf.result()) + } +} + +sealed abstract private[data] class NonEmptyLazyListInstances extends NonEmptyLazyListInstances1 { + + implicit val catsDataInstancesForNonEmptyLazyList: Bimonad[NonEmptyLazyList] with NonEmptyTraverse[NonEmptyLazyList] = + new AbstractNonEmptyBimonadTraverse[LazyList, NonEmptyLazyList] { + + def extract[A](fa: NonEmptyLazyList[A]): A = fa.head + + 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))(h => create(LazyList(h))) + case Some(gtail) => Apply[G].map2(f(fa.head), gtail)((h, t) => create(LazyList(h) ++ t)) + } + .value + + 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)) + }) + } + + implicit def catsDataOrderForNonEmptyLazyList[A: Order]: Order[NonEmptyLazyList[A]] = + Order[LazyList[A]].asInstanceOf[Order[NonEmptyLazyList[A]]] + + implicit def catsDataSemigroupForNonEmptyLazyList[A]: Semigroup[NonEmptyLazyList[A]] = + Semigroup[LazyList[A]].asInstanceOf[Semigroup[NonEmptyLazyList[A]]] + + implicit def catsDataShowForNonEmptyLazyList[A](implicit A: Show[A]): Show[NonEmptyLazyList[A]] = + Show.show[NonEmptyLazyList[A]](nec => s"NonEmpty${Show[LazyList[A]].show(nec.toLazyList)}") + +} + +sealed abstract private[data] class NonEmptyLazyListInstances1 extends NonEmptyLazyListInstances2 { + implicit val catsDataSemigroupKForNonEmptyLazyList: SemigroupK[NonEmptyLazyList] = + SemigroupK[LazyList].asInstanceOf[SemigroupK[NonEmptyLazyList]] + + implicit def catsDataHashForNonEmptyLazyList[A: Hash]: Hash[NonEmptyLazyList[A]] = + Hash[LazyList[A]].asInstanceOf[Hash[NonEmptyLazyList[A]]] + +} + +sealed abstract private[data] class NonEmptyLazyListInstances2 extends NonEmptyLazyListInstances3 { + implicit def catsDataPartialOrderForNonEmptyLazyList[A: PartialOrder]: PartialOrder[NonEmptyLazyList[A]] = + PartialOrder[LazyList[A]].asInstanceOf[PartialOrder[NonEmptyLazyList[A]]] +} + +sealed abstract private[data] class NonEmptyLazyListInstances3 { + implicit def catsDataEqForNonEmptyLazyList[A: Eq]: Eq[NonEmptyLazyList[A]] = + Eq[LazyList[A]].asInstanceOf[Eq[NonEmptyLazyList[A]]] +} diff --git a/core/src/main/scala-2.13+/cats/data/NonEmptyList.scala b/core/src/main/scala-2.13+/cats/data/NonEmptyList.scala new file mode 100644 index 0000000000..c843e46335 --- /dev/null +++ b/core/src/main/scala-2.13+/cats/data/NonEmptyList.scala @@ -0,0 +1,575 @@ +package cats +package data + +import cats.data.NonEmptyList.ZipNonEmptyList +import cats.instances.list._ + +import scala.collection.immutable.{SortedMap, SortedSet, TreeMap, TreeSet} +import scala.collection.mutable +import scala.collection.mutable.ListBuffer +import NonEmptyList.create + +import scala.annotation.tailrec + +/** + * A data type which represents a non empty list of A, with + * single element (head) and optional structure (tail). + */ +object NonEmptyList extends NonEmptyListInstances { + private[data] type Base + private[data] trait Tag extends Any + type Type[+A] <: Base with Tag + + private[cats] def create[A](s: List[A]): Type[A] = + s.asInstanceOf[Type[A]] + + private[cats] def unwrap[A](s: Type[A]): List[A] = + s.asInstanceOf[List[A]] + + def of[A](head: A, tail: A*): NonEmptyList[A] = apply(head, tail.toList) + + def apply[A](head: A, tail: List[A]): NonEmptyList[A] = create(head :: tail) + + def ofInitLast[A](init: List[A], last: A): NonEmptyList[A] = + create(init :+ last) + + def one[A](head: A): NonEmptyList[A] = create(List(head)) + + def unapply[A](nel: NonEmptyList[A]): Option[(A, List[A])] = + Some((nel.head, nel.tail)) + + /** + * Create a `NonEmptyList` from a `List`. + * + * The result will be `None` if the input list is empty and `Some` wrapping a + * `NonEmptyList` otherwise. + * + * @see [[fromListUnsafe]] for an unsafe version that throws an exception if + * the input list is empty. + */ + def fromList[A](l: List[A]): Option[NonEmptyList[A]] = + l match { + case Nil => None + case _ => Some(create(l)) + } + + /** + * Create a `NonEmptyList` from a `List`, or throw an + * `IllegalArgumentException` if the input list is empty. + * + * @see [[fromList]] for a safe version that returns `None` if the input list + * is empty. + */ + def fromListUnsafe[A](l: List[A]): NonEmptyList[A] = + l match { + case Nil => throw new IllegalArgumentException("Cannot create NonEmptyList from empty list") + case _ => create(l) + } + + def fromFoldable[F[_], A](fa: F[A])(implicit F: Foldable[F]): Option[NonEmptyList[A]] = + fromList(F.toList(fa)) + + def fromReducible[F[_], A](fa: F[A])(implicit F: Reducible[F]): NonEmptyList[A] = + F.toNonEmptyList(fa) + + class ZipNonEmptyList[A](val value: NonEmptyList[A]) extends Serializable + + object ZipNonEmptyList { + + def apply[A](nev: NonEmptyList[A]): ZipNonEmptyList[A] = + new ZipNonEmptyList(nev) + + implicit val catsDataCommutativeApplyForZipNonEmptyList: CommutativeApply[ZipNonEmptyList] = + new CommutativeApply[ZipNonEmptyList] { + def ap[A, B](ff: ZipNonEmptyList[A => B])(fa: ZipNonEmptyList[A]): ZipNonEmptyList[B] = + ZipNonEmptyList(ff.value.zipWith(fa.value)(_.apply(_))) + + override def map[A, B](fa: ZipNonEmptyList[A])(f: (A) => B): ZipNonEmptyList[B] = + ZipNonEmptyList(fa.value.map(f)) + + override def product[A, B](fa: ZipNonEmptyList[A], fb: ZipNonEmptyList[B]): ZipNonEmptyList[(A, B)] = + ZipNonEmptyList(fa.value.zipWith(fb.value) { case (a, b) => (a, b) }) + } + + implicit def zipNelEq[A: Eq]: Eq[ZipNonEmptyList[A]] = Eq.by(_.value) + } + + implicit def catsNonEmptyListOps[A](value: NonEmptyList[A]): NonEmptyListOps[A] = + new NonEmptyListOps(value) +} + +class NonEmptyListOps[A](private val value: NonEmptyList[A]) extends AnyVal { + + /** + * Return the head and tail into a single list + * {{{ + * scala> import cats.data.NonEmptyList + * scala> val nel = NonEmptyList.of(1, 2, 3, 4, 5) + * scala> nel.toList + * res0: scala.collection.immutable.List[Int] = List(1, 2, 3, 4, 5) + * }}} + */ + def toList: List[A] = NonEmptyList.unwrap(value) + + def head: A = toList.head + + def tail: List[A] = toList.tail + + /** + * Selects the last element + * {{{ + * scala> import cats.data.NonEmptyList + * scala> val nel = NonEmptyList.of(1, 2, 3, 4, 5) + * scala> nel.last + * res0: Int = 5 + * }}} + */ + def last: A = toList.last + + /** + * Selects all elements except the last + * + * {{{ + * scala> import cats.data.NonEmptyList + * scala> val nel = NonEmptyList.of(1, 2, 3, 4, 5) + * scala> nel.init + * res0: scala.collection.immutable.List[Int] = List(1, 2, 3, 4) + * }}} + */ + def init: List[A] = toList.init + + /** + * The size of this NonEmptyList + * + * {{{ + * scala> import cats.data.NonEmptyList + * scala> val nel = NonEmptyList.of(1, 2, 3, 4, 5) + * scala> nel.size + * res0: Int = 5 + * }}} + */ + def size: Int = toList.size + + def length: Int = size + + /** + * Applies f to all the elements of the structure + */ + def map[B](f: A => B): NonEmptyList[B] = create(toList.map(f)) + + def ++[AA >: A](l: List[AA]): NonEmptyList[AA] = + concat(l) + + def concat[AA >: A](other: List[AA]): NonEmptyList[AA] = + create(toList ::: other) + + /** + * Append another NonEmptyList + */ + def concatNel[AA >: A](other: NonEmptyList[AA]): NonEmptyList[AA] = + concat(other.toList) + + def flatMap[B](f: A => NonEmptyList[B]): NonEmptyList[B] = + create(toList.flatMap(f.andThen(_.toList))) + + def ::[AA >: A](a: AA): NonEmptyList[AA] = + prepend(a) + + def prepend[AA >: A](a: AA): NonEmptyList[AA] = + create(a :: toList) + + /** + * Alias for append + * + * {{{ + * scala> import cats.data.NonEmptyList + * scala> val nel = NonEmptyList.of(1, 2, 3) + * scala> nel :+ 4 + * res0: cats.data.NonEmptyList[Int] = NonEmptyList(1, 2, 3, 4) + * }}} + */ + def :+[AA >: A](a: AA): NonEmptyList[AA] = + append(a) + + def append[AA >: A](a: AA): NonEmptyList[AA] = + create(toList :+ a) + + /** + * Alias for concatNel + * + * {{{ + * scala> import cats.data.NonEmptyList + * scala> val nel = NonEmptyList.of(1, 2, 3) + * scala> nel ::: NonEmptyList.of(4, 5) + * res0: cats.data.NonEmptyList[Int] = NonEmptyList(1, 2, 3, 4, 5) + * }}} + */ + def :::[AA >: A](other: NonEmptyList[AA]): NonEmptyList[AA] = + other.concatNel(value) + + /** + * Remove elements not matching the predicate + * + * {{{ + * scala> import cats.data.NonEmptyList + * scala> val nel = NonEmptyList.of(1, 2, 3, 4, 5) + * scala> nel.filter(_ < 3) + * res0: scala.collection.immutable.List[Int] = List(1, 2) + * }}} + */ + def filter(p: A => Boolean): List[A] = + toList.filter(p) + + /** + * Remove elements matching the predicate + * + * {{{ + * scala> import cats.data.NonEmptyList + * scala> val nel = NonEmptyList.of(1, 2, 3, 4, 5) + * scala> nel.filterNot(_ < 3) + * res0: scala.collection.immutable.List[Int] = List(3, 4, 5) + * }}} + */ + def filterNot(p: A => Boolean): List[A] = + toList.filterNot(p) + + /** + * Builds a new `List` by applying a partial function to + * all the elements from this `NonEmptyList` on which the function is defined + * + * {{{ + * scala> import cats.data.NonEmptyList + * scala> val nel = NonEmptyList.of(1, 2, 3, 4, 5) + * scala> nel.collect { case v if v < 3 => v } + * res0: scala.collection.immutable.List[Int] = List(1, 2) + * scala> nel.collect { + * | case v if v % 2 == 0 => "even" + * | case _ => "odd" + * | } + * res1: scala.collection.immutable.List[String] = List(odd, even, odd, even, odd) + * }}} + */ + def collect[B](pf: PartialFunction[A, B]): List[B] = + toList.collect(pf) + + /** + * Find the first element matching the predicate, if one exists + */ + def find(p: A => Boolean): Option[A] = + toList.find(p) + + /** + * Check whether at least one element satisfies the predicate + */ + def exists(p: A => Boolean): Boolean = + toList.exists(p) + + /** + * Check whether all elements satisfy the predicate + */ + def forall(p: A => Boolean): Boolean = + toList.forall(p) + + /** + * Left-associative fold on the structure using f. + */ + def foldLeft[B](b: B)(f: (B, A) => B): B = + toList.foldLeft(b)(f) + + /** + * Right-associative fold on the structure using f. + */ + def foldRight[B](lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = + Foldable[List].foldRight(toList, lb)(f) + + /** + * Left-associative reduce using f. + */ + def reduceLeft[AA >: A](f: (AA, AA) => AA): AA = + toList.reduceLeft(f) + + /** + * Reduce using the `Semigroup` of `AA`. + */ + def reduce[AA >: A](implicit S: Semigroup[AA]): AA = + S.combineAllOption(toList).get + + def traverse[G[_], B](f: A => G[B])(implicit G: Applicative[G]): G[NonEmptyList[B]] = + Traverse[List].traverse(toList)(f).asInstanceOf[G[NonEmptyList[B]]] + + def coflatMap[B](f: NonEmptyList[A] => B): NonEmptyList[B] = + create(CoflatMap[List].coflatMap(toList)(f.asInstanceOf[List[A] => B])) + + def ===[AA >: A](o: NonEmptyList[AA])(implicit AA: Eq[AA]): Boolean = + Eq[List[AA]].eqv(toList, o.toList) + + def show[AA >: A](implicit AA: Show[AA]): String = + s"NonEmpty${Show[List[AA]].show(toList)}" + + /** + * Remove duplicates. Duplicates are checked using `Order[_]` instance. + */ + def distinct[AA >: A](implicit O: Order[AA]): NonEmptyList[AA] = { + implicit val ord = O.toOrdering + + val buf = ListBuffer.empty[AA] + toList.foldLeft(TreeSet.empty[AA]) { (elementsSoFar, b) => + if (elementsSoFar(b)) elementsSoFar + else { + buf += b; elementsSoFar + b + } + } + + create(buf.toList) + } + + /** + * Apply `f` to the "initial element" of this LazyList and lazily combine it + * with every other value using the given function `g`. + */ + final def reduceLeftTo[B](f: A => B)(g: (B, A) => B): B = { + val iter = toList.iterator + var result = f(iter.next) + while (iter.hasNext) { result = g(result, iter.next) } + result + } + + /** + * Right-associative reduce using f. + */ + final def reduceRight[AA >: A](f: (A, AA) => AA): AA = + toList.reduceRight(f) + + /** + * Apply `f` to the "initial element" of this NonEmptyLazyList and + * lazily combine it with every other value using the given function `g`. + */ + final def reduceRightTo[B](f: A => B)(g: (A, B) => B): B = { + val iter = toList.reverseIterator + var result = f(iter.next) + while (iter.hasNext) { result = g(iter.next, result) } + result + } + + /** + * Reverse this `NonEmptyList`. + * + * {{{ + * scala> import cats.data.NonEmptyList + * scala> val nel = NonEmptyList.of(1, 2, 3) + * scala> nel.reverse + * res0: cats.data.NonEmptyList[Int] = NonEmptyList(3, 2, 1) + * }}} + */ + def reverse: NonEmptyList[A] = + create(toList.reverse) + + /** + * Zips this `NonEmptyList` with another `NonEmptyList` and applies a function for each pair of elements. + * + * {{{ + * scala> import cats.data.NonEmptyList + * scala> val as = NonEmptyList.of(1, 2, 3) + * scala> val bs = NonEmptyList.of("A", "B", "C") + * scala> as.zipWith(bs)(_ + _) + * res0: cats.data.NonEmptyList[String] = NonEmptyList(1A, 2B, 3C) + * }}} + */ + def zipWith[B, C](b: NonEmptyList[B])(f: (A, B) => C): NonEmptyList[C] = { + @tailrec + def zwRev(as: List[A], bs: List[B], acc: List[C]): List[C] = (as, bs) match { + case (Nil, _) => acc + case (_, Nil) => acc + case (x :: xs, y :: ys) => zwRev(xs, ys, f(x, y) :: acc) + } + + create(zwRev(toList, b.toList, Nil).reverse) + } + + /** + * Zips each element of this `NonEmptyList` with its index. + * + * {{{ + * scala> import cats.data.NonEmptyList + * scala> val nel = NonEmptyList.of("a", "b", "c") + * scala> nel.zipWithIndex + * res0: cats.data.NonEmptyList[(String, Int)] = NonEmptyList((a,0), (b,1), (c,2)) + * }}} + */ + def zipWithIndex: NonEmptyList[(A, Int)] = + create(toList.zipWithIndex) + + /** + * Sorts this `NonEmptyList` according to an `Order` on transformed `B` from `A` + * + * {{{ + * scala> import cats.data.NonEmptyList + * scala> import cats.instances.int._ + * scala> val nel = NonEmptyList.of(('a', 4), ('z', 1), ('e', 22)) + * scala> nel.sortBy(_._2) + * res0: cats.data.NonEmptyList[(Char, Int)] = NonEmptyList((z,1), (a,4), (e,22)) + * }}} + */ + def sortBy[B](f: A => B)(implicit B: Order[B]): NonEmptyList[A] = + // safe: sorting a NonEmptyList cannot produce an empty List + NonEmptyList.fromListUnsafe(toList.sortBy(f)(B.toOrdering)) + + /** + * Sorts this `NonEmptyList` according to an `Order` + * + * {{{ + * scala> import cats.data.NonEmptyList + * scala> import cats.instances.int._ + * scala> val nel = NonEmptyList.of(12, 4, 3, 9) + * scala> nel.sorted + * res0: cats.data.NonEmptyList[Int] = NonEmptyList(3, 4, 9, 12) + * }}} + */ + def sorted[AA >: A](implicit AA: Order[AA]): NonEmptyList[AA] = + // safe: sorting a NonEmptyList cannot produce an empty List + NonEmptyList.fromListUnsafe(toList.sorted(AA.toOrdering)) + + /** + * Groups elements inside this `NonEmptyList` according to the `Order` + * of the keys produced by the given mapping function. + * + * {{{ + * scala> import scala.collection.immutable.SortedMap + * scala> import cats.data.NonEmptyList + * scala> import cats.instances.boolean._ + * scala> val nel = NonEmptyList.of(12, -2, 3, -5) + * scala> nel.groupBy(_ >= 0) + * res0: SortedMap[Boolean, cats.data.NonEmptyList[Int]] = Map(false -> NonEmptyList(-2, -5), true -> NonEmptyList(12, 3)) + * }}} + */ + def groupBy[B](f: A => B)(implicit B: Order[B]): SortedMap[B, NonEmptyList[A]] = { + implicit val ordering: Ordering[B] = B.toOrdering + var m = TreeMap.empty[B, mutable.Builder[A, List[A]]] + + for { elem <- toList } { + 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, NonEmptyList.fromListUnsafe(v.result)) + }: TreeMap[B, NonEmptyList[A]] + } + + /** + * Groups elements inside this `NonEmptyList` according to the `Order` + * of the keys produced by the given mapping function. + * + * {{{ + * scala> import cats.data._ + * scala> import cats.instances.boolean._ + * scala> val nel = NonEmptyList.of(12, -2, 3, -5) + * scala> nel.groupByNem(_ >= 0) + * res0: NonEmptyMap[Boolean, NonEmptyList[Int]] = Map(false -> NonEmptyList(-2, -5), true -> NonEmptyList(12, 3)) + * }}} + */ + def groupByNem[B](f: A => B)(implicit B: Order[B]): NonEmptyMap[B, NonEmptyList[A]] = + NonEmptyMap.fromMapUnsafe(groupBy(f)) + + /** + * Creates new `NonEmptyMap`, similarly to List#toMap from scala standard library. + *{{{ + * scala> import cats.data._ + * scala> import cats.instances.int._ + * scala> val nel = NonEmptyList((0, "a"), List((1, "b"),(0, "c"), (2, "d"))) + * scala> nel.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(toList.map(ev): _*)(order.toOrdering)) + + /** + * Creates new `NonEmptySet`, similarly to List#toSet from scala standard library. + *{{{ + * scala> import cats.data._ + * scala> import cats.instances.int._ + * scala> val nel = NonEmptyList(1, List(2,2,3,4)) + * scala> nel.toNes + * res0: cats.data.NonEmptySet[Int] = TreeSet(1, 2, 3, 4) + *}}} + */ + def toNes[B >: A](implicit order: Order[B]): NonEmptySet[B] = + NonEmptySet.fromSetUnsafe(SortedSet[B](toList: _*)(order.toOrdering)) +} + +sealed abstract private[data] class NonEmptyListInstances extends NonEmptyListInstances1 { + + implicit val catsDataInstancesForNonEmptyList: Bimonad[NonEmptyList] with NonEmptyTraverse[NonEmptyList] = + new AbstractNonEmptyBimonadTraverse[List, NonEmptyList] { + + def extract[A](fa: NonEmptyList[A]): A = fa.head + + def nonEmptyTraverse[G[_]: Apply, A, B](fa: NonEmptyList[A])(f: A => G[B]): G[NonEmptyList[B]] = + Foldable[List] + .reduceRightToOption[A, G[List[B]]](fa.tail)(a => Apply[G].map(f(a))(List.apply(_))) { (a, lglb) => + Apply[G].map2Eval(f(a), lglb)(_ +: _) + } + .map { + case None => Apply[G].map(f(fa.head))(h => create(List(h))) + case Some(gtail) => Apply[G].map2(f(fa.head), gtail)((h, t) => create(List(h) ++ t)) + } + .value + + def reduceLeftTo[A, B](fa: NonEmptyList[A])(f: A => B)(g: (B, A) => B): B = fa.reduceLeftTo(f)(g) + + def reduceRightTo[A, B](fa: NonEmptyList[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)) + }) + + } + + implicit def catsDataOrderForNonEmptyList[A: Order]: Order[NonEmptyList[A]] = + Order[List[A]].asInstanceOf[Order[NonEmptyList[A]]] + + implicit def catsDataSemigroupForNonEmptyList[A]: Semigroup[NonEmptyList[A]] = + Semigroup[List[A]].asInstanceOf[Semigroup[NonEmptyList[A]]] + + implicit def catsDataShowForNonEmptyList[A](implicit A: Show[A]): Show[NonEmptyList[A]] = + Show.show[NonEmptyList[A]](_.show) + + implicit def catsDataNonEmptyParallelForNonEmptyList[A]: NonEmptyParallel[NonEmptyList, ZipNonEmptyList] = + new NonEmptyParallel[NonEmptyList, ZipNonEmptyList] { + + def flatMap: FlatMap[NonEmptyList] = NonEmptyList.catsDataInstancesForNonEmptyList + + def apply: Apply[ZipNonEmptyList] = ZipNonEmptyList.catsDataCommutativeApplyForZipNonEmptyList + + def sequential: ZipNonEmptyList ~> NonEmptyList = + λ[ZipNonEmptyList ~> NonEmptyList](_.value) + + def parallel: NonEmptyList ~> ZipNonEmptyList = + λ[NonEmptyList ~> ZipNonEmptyList](nel => new ZipNonEmptyList(nel)) + } + +} + +sealed abstract private[data] class NonEmptyListInstances1 extends NonEmptyListInstances2 { + implicit val catsDataSemigroupKForNonEmptyList: SemigroupK[NonEmptyList] = + SemigroupK[List].asInstanceOf[SemigroupK[NonEmptyList]] + + implicit def catsDataHashForNonEmptyList[A: Hash]: Hash[NonEmptyList[A]] = + Hash[List[A]].asInstanceOf[Hash[NonEmptyList[A]]] + +} + +sealed abstract private[data] class NonEmptyListInstances2 extends NonEmptyListInstances3 { + implicit def catsDataPartialOrderForNonEmptyList[A: PartialOrder]: PartialOrder[NonEmptyList[A]] = + PartialOrder[List[A]].asInstanceOf[PartialOrder[NonEmptyList[A]]] +} + +sealed abstract private[data] class NonEmptyListInstances3 { + implicit def catsDataEqForNonEmptyList[A: Eq]: Eq[NonEmptyList[A]] = + Eq[List[A]].asInstanceOf[Eq[NonEmptyList[A]]] +} diff --git a/core/src/main/scala-2.13+/cats/data/NonEmptyVector.scala b/core/src/main/scala-2.13+/cats/data/NonEmptyVector.scala new file mode 100644 index 0000000000..382ea0ffe0 --- /dev/null +++ b/core/src/main/scala-2.13+/cats/data/NonEmptyVector.scala @@ -0,0 +1,427 @@ +package cats +package data + +import cats.data.NonEmptyVector.ZipNonEmptyVector + +import scala.collection.immutable.TreeSet +import NonEmptyVector.create +import instances.vector._ + +object NonEmptyVector extends NonEmptyVectorInstances { + private[data] type Base + private[data] trait Tag extends Any + type Type[+A] <: Base with Tag + + private[cats] def create[A](s: Vector[A]): Type[A] = + s.asInstanceOf[Type[A]] + + private[cats] def unwrap[A](s: Type[A]): Vector[A] = + s.asInstanceOf[Vector[A]] + + def fromVector[A](v: Vector[A]): Option[NonEmptyVector[A]] = + if (v.nonEmpty) Option(create(v)) else None + + def fromNonEmptyList[A](as: NonEmptyList[A]): NonEmptyVector[A] = + create(Vector.from(as.toList)) + + def fromSeq[A](as: Seq[A]): Option[NonEmptyVector[A]] = + if (as.nonEmpty) Option(create(Vector.from(as))) else None + + def fromVectorPrepend[A](a: A, ca: Vector[A]): NonEmptyVector[A] = + create(a +: ca) + + def fromVectorAppend[A](ca: Vector[A], a: A): NonEmptyVector[A] = + create(ca :+ a) + + def apply[A](head: A, tail: Vector[A]): NonEmptyVector[A] = + create(head +: tail) + + def of[A](head: A, tail: A*): NonEmptyVector[A] = { + val buf = Vector.newBuilder[A] + buf += head + tail.foreach(buf += _) + create(buf.result) + } + + def one[A](head: A): NonEmptyVector[A] = apply(head, Vector.empty[A]) + + def unapply[A](nev: NonEmptyVector[A]): Some[(A, Vector[A])] = Some((nev.head, nev.tail)) + + def fromVectorUnsafe[A](vector: Vector[A]): NonEmptyVector[A] = + if (vector.nonEmpty) create(vector) + else throw new IllegalArgumentException("Cannot create NonEmptyVector from empty vector") + + class ZipNonEmptyVector[A](val value: NonEmptyVector[A]) extends Serializable + + object ZipNonEmptyVector { + + def apply[A](nev: NonEmptyVector[A]): ZipNonEmptyVector[A] = + new ZipNonEmptyVector(nev) + + implicit val catsDataCommutativeApplyForZipNonEmptyVector: CommutativeApply[ZipNonEmptyVector] = + new CommutativeApply[ZipNonEmptyVector] { + def ap[A, B](ff: ZipNonEmptyVector[A => B])(fa: ZipNonEmptyVector[A]): ZipNonEmptyVector[B] = + ZipNonEmptyVector(ff.value.zipWith(fa.value)(_.apply(_))) + + override def map[A, B](fa: ZipNonEmptyVector[A])(f: (A) => B): ZipNonEmptyVector[B] = + ZipNonEmptyVector(fa.value.map(f)) + + override def product[A, B](fa: ZipNonEmptyVector[A], fb: ZipNonEmptyVector[B]): ZipNonEmptyVector[(A, B)] = + ZipNonEmptyVector(fa.value.zipWith(fb.value) { case (a, b) => (a, b) }) + } + + implicit def zipNevEq[A: Eq]: Eq[ZipNonEmptyVector[A]] = Eq.by(_.value) + } + + implicit def catsNonEmptyVectorOps[A](value: NonEmptyVector[A]): NonEmptyVectorOps[A] = + new NonEmptyVectorOps(value) +} + +/** + * A data type which represents a `Vector` guaranteed to contain at least one element. + *
+ * Note that the constructor is `private` to prevent accidental construction of an empty + * `NonEmptyVector`. However, due to https://issues.scala-lang.org/browse/SI-6601, on + * Scala 2.10, this may be bypassed due to a compiler bug. + */ +class NonEmptyVectorOps[+A](private val value: NonEmptyVector[A]) extends AnyVal { + + final def toVector: Vector[A] = NonEmptyVector.unwrap(value) + + /** Gets the element at the index, if it exists */ + def get(i: Int): Option[A] = + toVector.lift(i) + + /** Gets the element at the index, or throws an exception if none exists */ + def getUnsafe(i: Int): A = toVector(i) + + /** Updates the element at the index, if it exists */ + def updated[AA >: A](i: Int, a: AA): Option[NonEmptyVector[AA]] = + if (toVector.isDefinedAt(i)) Some(create(toVector.updated(i, a))) else None + + /** + * Updates the element at the index, or throws an `IndexOutOfBoundsException` + * if none exists (if `i` does not satisfy `0 <= i < length`). + */ + def updatedUnsafe[AA >: A](i: Int, a: AA): NonEmptyVector[AA] = create(toVector.updated(i, a)) + + def head: A = toVector.head + + def tail: Vector[A] = toVector.tail + + def last: A = toVector.last + + def init: Vector[A] = toVector.init + + /** + * Remove elements not matching the predicate + * + * {{{ + * scala> import cats.data.NonEmptyVector + * scala> val nev = NonEmptyVector.of(1, 2, 3, 4, 5) + * scala> nev.filter(_ < 3) + * res0: scala.collection.immutable.Vector[Int] = Vector(1, 2) + * }}} + */ + def filter(f: A => Boolean): Vector[A] = toVector.filter(f) + + /** + * Remove elements matching the predicate + * + * {{{ + * scala> import cats.data.NonEmptyVector + * scala> val nev = NonEmptyVector.of(1, 2, 3, 4, 5) + * scala> nev.filterNot(_ < 3) + * res0: scala.collection.immutable.Vector[Int] = Vector(3, 4, 5) + * }}} + */ + def filterNot(f: A => Boolean): Vector[A] = toVector.filterNot(f) + + def collect[B](pf: PartialFunction[A, B]): Vector[B] = toVector.collect(pf) + + /** + * Alias for [[concat]] + */ + def ++[AA >: A](other: Vector[AA]): NonEmptyVector[AA] = concat(other) + + /** + * Append this NEV to another NEV, producing a new `NonEmptyVector`. + * + * {{{ + * scala> import cats.data.NonEmptyVector + * scala> val nev = NonEmptyVector.of(1, 2, 3) + * scala> nev ++: NonEmptyVector.of(4, 5) + * res0: cats.data.NonEmptyVector[Int] = NonEmptyVector(1, 2, 3, 4, 5) + * }}} + */ + def ++:[AA >: A](other: NonEmptyVector[AA]): NonEmptyVector[AA] = other.concatNev(value) + + /** + * Append another `Vector` to this, producing a new `NonEmptyVector`. + */ + def concat[AA >: A](other: Vector[AA]): NonEmptyVector[AA] = create(toVector ++ other) + + /** + * Append another `NonEmptyVector` to this, producing a new `NonEmptyVector`. + */ + def concatNev[AA >: A](other: NonEmptyVector[AA]): NonEmptyVector[AA] = create(toVector ++ other.toVector) + + /** + * Append an item to this, producing a new `NonEmptyVector`. + */ + def append[AA >: A](a: AA): NonEmptyVector[AA] = create(toVector :+ a) + + /** + * Alias for [[append]] + */ + def :+[AA >: A](a: AA): NonEmptyVector[AA] = append(a) + + /** + * Prepend an item to this, producing a new `NonEmptyVector`. + */ + def prepend[AA >: A](a: AA): NonEmptyVector[AA] = create(a +: toVector) + + /** + * Alias for [[prepend]] + */ + def +:[AA >: A](a: AA): NonEmptyVector[AA] = prepend(a) + + /** + * Find the first element matching the predicate, if one exists + */ + def find(f: A => Boolean): Option[A] = toVector.find(f) + + /** + * Check whether at least one element satisfies the predicate. + */ + def exists(f: A => Boolean): Boolean = toVector.exists(f) + + /** + * Check whether all elements satisfy the predicate. + */ + def forall(f: A => Boolean): Boolean = toVector.forall(f) + + /** + * Left-associative fold using f. + */ + def foldLeft[B](b: B)(f: (B, A) => B): B = + toVector.foldLeft(b)(f) + + /** + * Right-associative fold using f. + */ + def foldRight[B](lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = + Foldable[Vector].foldRight(toVector, lb)(f) + + /** + * Applies f to all the elements + */ + def map[B](f: A => B): NonEmptyVector[B] = + create(toVector.map(f)) + + /** + * Applies f to all elements and combines the result + */ + def flatMap[B](f: A => NonEmptyVector[B]): NonEmptyVector[B] = + create(toVector.flatMap(a => f(a).toVector)) + + /** + * Left-associative reduce using f. + */ + def reduceLeft[AA >: A](f: (AA, AA) => AA): AA = + toVector.reduceLeft(f) + + /** + * Apply `f` to the "initial element" of this Vector and lazily combine it + * with every other value using the given function `g`. + */ + final def reduceLeftTo[B](f: A => B)(g: (B, A) => B): B = { + val iter = toVector.iterator + var result = f(iter.next) + while (iter.hasNext) { result = g(result, iter.next) } + result + } + + /** + * Right-associative reduce using f. + */ + final def reduceRight[AA >: A](f: (A, AA) => AA): AA = + toVector.reduceRight(f) + + /** + * Apply `f` to the "initial element" of this NonEmptyVector and + * lazily combine it with every other value using the given function `g`. + */ + final def reduceRightTo[B](f: A => B)(g: (A, B) => B): B = { + val iter = toVector.reverseIterator + var result = f(iter.next) + while (iter.hasNext) { result = g(iter.next, result) } + result + } + + def toNonEmptyList: NonEmptyList[A] = + NonEmptyList.fromListUnsafe(toVector.toList) + + /** + * Reduce using the Semigroup of A + */ + def reduce[AA >: A](implicit S: Semigroup[AA]): AA = + S.combineAllOption(toVector).get + + /** + * Typesafe equality operator. + * + * This method is similar to == except that it only allows two + * NonEmptyVector[A] values to be compared to each other, and uses + * equality provided by Eq[_] instances, rather than using the + * universal equality provided by .equals. + */ + def ===[AA >: A](that: NonEmptyVector[AA])(implicit A: Eq[AA]): Boolean = + Eq[Vector[AA]].eqv(toVector, that.toVector) + + /** + * Typesafe stringification method. + * + * This method is similar to .toString except that it stringifies + * values according to Show[_] instances, rather than using the + * universal .toString method. + */ + def show[AA >: A](implicit AA: Show[AA]): String = + s"NonEmpty${Show[Vector[AA]].show(toVector)}" + + def length: Int = toVector.length + + /** + * Remove duplicates. Duplicates are checked using `Order[_]` instance. + */ + def distinct[AA >: A](implicit O: Order[AA]): NonEmptyVector[AA] = { + implicit val ord = O.toOrdering + + val buf = Vector.newBuilder[AA] + toVector.foldLeft(TreeSet.empty[AA]) { (elementsSoFar, a) => + if (elementsSoFar(a)) elementsSoFar + else { + buf += a; elementsSoFar + a + } + } + + create(buf.result()) + } + + /** + * Zips this `NonEmptyVector` with another `NonEmptyVector` and applies a function for each pair of elements. + * + * {{{ + * scala> import cats.data.NonEmptyVector + * scala> val as = NonEmptyVector.of(1, 2, 3) + * scala> val bs = NonEmptyVector.of("A", "B", "C") + * scala> as.zipWith(bs)(_ + _) + * res0: cats.data.NonEmptyVector[String] = NonEmptyVector(1A, 2B, 3C) + * }}} + */ + def zipWith[B, C](b: NonEmptyVector[B])(f: (A, B) => C): NonEmptyVector[C] = + NonEmptyVector.fromVectorUnsafe(toVector.lazyZip(b.toVector).map(f)) + + def reverse: NonEmptyVector[A] = + create(toVector.reverse) + + def zipWithIndex: NonEmptyVector[(A, Int)] = + create(toVector.zipWithIndex) + + def sortBy[B](f: A => B)(implicit B: Order[B]): NonEmptyVector[A] = + create(toVector.sortBy(f)(B.toOrdering)) + + def sorted[AA >: A](implicit AA: Order[AA]): NonEmptyVector[AA] = + create(toVector.sorted(AA.toOrdering)) +} + +sealed abstract private[data] class NonEmptyVectorInstances extends NonEmptyVectorInstances1 { + + implicit val catsDataInstancesForNonEmptyVector: Bimonad[NonEmptyVector] with NonEmptyTraverse[NonEmptyVector] = + new AbstractNonEmptyBimonadTraverse[Vector, NonEmptyVector] { + + def extract[A](fa: NonEmptyVector[A]): A = fa.head + + def nonEmptyTraverse[G[_], A, B]( + nel: NonEmptyVector[A] + )(f: A => G[B])(implicit G: Apply[G]): G[NonEmptyVector[B]] = + Foldable[Vector] + .reduceRightToOption[A, G[Vector[B]]](nel.tail)(a => G.map(f(a))(_ +: Vector.empty)) { (a, lglb) => + G.map2Eval(f(a), lglb)(_ +: _) + } + .map { + case None => G.map(f(nel.head))(NonEmptyVector(_, Vector.empty)) + case Some(gtail) => G.map2(f(nel.head), gtail)(NonEmptyVector(_, _)) + } + .value + + def reduceLeftTo[A, B](fa: NonEmptyVector[A])(f: A => B)(g: (B, A) => B): B = fa.reduceLeftTo(f)(g) + + def reduceRightTo[A, B](fa: NonEmptyVector[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 toNonEmptyList[A](fa: NonEmptyVector[A]): NonEmptyList[A] = + fa.toNonEmptyList + + override def nonEmptyPartition[A, B, C]( + fa: NonEmptyVector[A] + )(f: (A) => Either[B, C]): Ior[NonEmptyList[B], NonEmptyList[C]] = { + import cats.syntax.either._ + + reduceLeftTo(fa)(a => f(a).bimap(NonEmptyVector.one, NonEmptyVector.one).toIor)( + (ior, a) => + (f(a), ior) match { + case (Right(c), Ior.Left(_)) => ior.putRight(NonEmptyVector.one(c)) + case (Right(c), _) => ior.map(_ :+ c) + case (Left(b), Ior.Right(_)) => ior.putLeft(NonEmptyVector.one(b)) + case (Left(b), _) => ior.leftMap(_ :+ b) + } + ).bimap(_.toNonEmptyList, _.toNonEmptyList) + + } + } + + implicit def catsDataParallelForNonEmptyVector[A]: NonEmptyParallel[NonEmptyVector, ZipNonEmptyVector] = + new NonEmptyParallel[NonEmptyVector, ZipNonEmptyVector] { + + def apply: Apply[ZipNonEmptyVector] = ZipNonEmptyVector.catsDataCommutativeApplyForZipNonEmptyVector + def flatMap: FlatMap[NonEmptyVector] = catsDataInstancesForNonEmptyVector + + def sequential: ZipNonEmptyVector ~> NonEmptyVector = + λ[ZipNonEmptyVector ~> NonEmptyVector](_.value) + + def parallel: NonEmptyVector ~> ZipNonEmptyVector = + λ[NonEmptyVector ~> ZipNonEmptyVector](nev => new ZipNonEmptyVector(nev)) + } + + implicit def catsDataOrderForNonEmptyVector[A: Order]: Order[NonEmptyVector[A]] = + Order[Vector[A]].asInstanceOf[Order[NonEmptyVector[A]]] + + implicit def catsDataSemigroupForNonEmptyVector[A]: Semigroup[NonEmptyVector[A]] = + Semigroup[Vector[A]].asInstanceOf[Semigroup[NonEmptyVector[A]]] + + implicit def catsDataShowForNonEmptyVector[A](implicit A: Show[A]): Show[NonEmptyVector[A]] = + Show.show[NonEmptyVector[A]](_.show) + +} + +sealed abstract private[data] class NonEmptyVectorInstances1 extends NonEmptyVectorInstances2 { + implicit val catsDataSemigroupKForNonEmptyVector: SemigroupK[NonEmptyVector] = + SemigroupK[Vector].asInstanceOf[SemigroupK[NonEmptyVector]] + + implicit def catsDataHashForNonEmptyVector[A: Hash]: Hash[NonEmptyVector[A]] = + Hash[Vector[A]].asInstanceOf[Hash[NonEmptyVector[A]]] + +} + +sealed abstract private[data] class NonEmptyVectorInstances2 extends NonEmptyVectorInstances3 { + implicit def catsDataPartialOrderForNonEmptyVector[A: PartialOrder]: PartialOrder[NonEmptyVector[A]] = + PartialOrder[Vector[A]].asInstanceOf[PartialOrder[NonEmptyVector[A]]] +} + +sealed abstract private[data] class NonEmptyVectorInstances3 { + implicit def catsDataEqForNonEmptyVector[A: Eq]: Eq[NonEmptyVector[A]] = + Eq[Vector[A]].asInstanceOf[Eq[NonEmptyVector[A]]] +} diff --git a/core/src/main/scala-2.13+/cats/data/ScalaVersionSpecificPackage.scala b/core/src/main/scala-2.13+/cats/data/ScalaVersionSpecificPackage.scala new file mode 100644 index 0000000000..5eee148dbd --- /dev/null +++ b/core/src/main/scala-2.13+/cats/data/ScalaVersionSpecificPackage.scala @@ -0,0 +1,12 @@ +package cats + +package data + +abstract private[data] class ScalaVersionSpecificPackage { + type NonEmptyLazyList[+A] = NonEmptyLazyList.Type[A] + + type NonEmptyList[+A] = NonEmptyList.Type[A] + + type NonEmptyVector[+A] = NonEmptyVector.Type[A] + +} diff --git a/core/src/main/scala/cats/data/AbstractNonEmptyBimonadTraverse.scala b/core/src/main/scala/cats/data/AbstractNonEmptyBimonadTraverse.scala new file mode 100644 index 0000000000..f19bef7839 --- /dev/null +++ b/core/src/main/scala/cats/data/AbstractNonEmptyBimonadTraverse.scala @@ -0,0 +1,75 @@ +package cats +package data + +abstract private[data] class AbstractNonEmptyBimonadTraverse[F[_], NonEmptyF[_]](implicit MF: Monad[F], + CF: CoflatMap[F], + TF: Traverse[F]) + extends Bimonad[NonEmptyF] + with NonEmptyTraverse[NonEmptyF] { + val monadInstance = MF.asInstanceOf[Monad[NonEmptyF]] + val coflatMapInstance = CF.asInstanceOf[CoflatMap[NonEmptyF]] + val traverseInstance = Traverse[F].asInstanceOf[Traverse[NonEmptyF]] + + def pure[A](x: A): NonEmptyF[A] = monadInstance.pure(x) + + override def map[A, B](fa: NonEmptyF[A])(f: A => B): NonEmptyF[B] = monadInstance.map(fa)(f) + + def flatMap[A, B](fa: NonEmptyF[A])(f: A => NonEmptyF[B]): NonEmptyF[B] = + monadInstance.flatMap(fa)(f) + + override def map2[A, B, Z](fa: NonEmptyF[A], fb: NonEmptyF[B])(f: (A, B) => Z): NonEmptyF[Z] = + monadInstance.map2(fa, fb)(f) + + override def map2Eval[A, B, Z](fa: NonEmptyF[A], fb: Eval[NonEmptyF[B]])(f: (A, B) => Z): Eval[NonEmptyF[Z]] = + monadInstance.map2Eval(fa, fb)(f) + + def coflatMap[A, B](fa: NonEmptyF[A])(f: NonEmptyF[A] => B): NonEmptyF[B] = + coflatMapInstance.coflatMap(fa)(f) + + def tailRecM[A, B](a: A)(f: A => NonEmptyF[Either[A, B]]): NonEmptyF[B] = + monadInstance.tailRecM(a)(f) + + def foldLeft[A, B](fa: NonEmptyF[A], b: B)(f: (B, A) => B): B = + traverseInstance.foldLeft(fa, b)(f) + + def foldRight[A, B](fa: NonEmptyF[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = + traverseInstance.foldRight(fa, lb)(f) + + override def foldMap[A, B](fa: NonEmptyF[A])(f: A => B)(implicit B: Monoid[B]): B = + traverseInstance.foldMap(fa)(f) + + override def traverse[G[_], A, B](fa: NonEmptyF[A])(f: A => G[B])(implicit G: Applicative[G]): G[NonEmptyF[B]] = + traverseInstance.traverse(fa)(f) + + override def mapWithIndex[A, B](fa: NonEmptyF[A])(f: (A, Int) => B): NonEmptyF[B] = + traverseInstance.mapWithIndex(fa)(f) + + override def zipWithIndex[A](fa: NonEmptyF[A]): NonEmptyF[(A, Int)] = traverseInstance.zipWithIndex(fa) + + override def exists[A](fa: NonEmptyF[A])(p: A => Boolean): Boolean = traverseInstance.exists(fa)(p) + + override def forall[A](fa: NonEmptyF[A])(p: A => Boolean): Boolean = traverseInstance.forall(fa)(p) + + override def get[A](fa: NonEmptyF[A])(idx: Long): Option[A] = traverseInstance.get(fa)(idx) + + override def isEmpty[A](fa: NonEmptyF[A]): Boolean = false + + override def foldM[G[_], A, B](fa: NonEmptyF[A], z: B)(f: (B, A) => G[B])(implicit G: Monad[G]): G[B] = + traverseInstance.foldM(fa, z)(f) + + override def fold[A](fa: NonEmptyF[A])(implicit A: Monoid[A]): A = traverseInstance.fold(fa) + + override def toList[A](fa: NonEmptyF[A]): List[A] = traverseInstance.toList(fa) + + override def reduceLeftOption[A](fa: NonEmptyF[A])(f: (A, A) => A): Option[A] = + traverseInstance.reduceLeftOption(fa)(f) + + override def find[A](fa: NonEmptyF[A])(f: A => Boolean): Option[A] = traverseInstance.find(fa)(f) + + override def collectFirst[A, B](fa: NonEmptyF[A])(pf: PartialFunction[A, B]): Option[B] = + traverseInstance.collectFirst(fa)(pf) + + override def collectFirstSome[A, B](fa: NonEmptyF[A])(f: A => Option[B]): Option[B] = + traverseInstance.collectFirstSome(fa)(f) + +} diff --git a/core/src/main/scala/cats/data/NonEmptyChain.scala b/core/src/main/scala/cats/data/NonEmptyChain.scala index bce726b7b4..97bb193629 100644 --- a/core/src/main/scala/cats/data/NonEmptyChain.scala +++ b/core/src/main/scala/cats/data/NonEmptyChain.scala @@ -17,16 +17,13 @@ package cats package data -import NonEmptyChainImpl.{create, unwrap} -import cats.Order +import NonEmptyChainImpl.create +import cats.{Order, Semigroup} import cats.kernel._ -import scala.annotation.tailrec import scala.collection.immutable._ -import scala.collection.mutable.ListBuffer private[data] object NonEmptyChainImpl extends NonEmptyChainInstances { - private[data] type Base private[data] trait Tag extends Any type Type[+A] <: Base with Tag @@ -388,24 +385,48 @@ class NonEmptyChainOps[A](private val value: NonEmptyChain[A]) extends AnyVal { /** * Remove duplicates. Duplicates are checked using `Order[_]` instance. */ - final def distinct[AA >: A](implicit O: Order[AA]): NonEmptyChain[AA] = { - implicit val ord = O.toOrdering - - var alreadyIn = TreeSet(head: AA) + final def distinct[AA >: A](implicit O: Order[AA]): NonEmptyChain[AA] = + create(toChain.distinct[AA]) - foldLeft(NonEmptyChain(head: AA)) { (elementsSoFar, b) => - if (alreadyIn.contains(b)) { - elementsSoFar - } else { - alreadyIn += b - elementsSoFar :+ b - } - } - } } sealed abstract private[data] class NonEmptyChainInstances extends NonEmptyChainInstances1 { - implicit val catsDataInstancesForNonEmptyChain + + implicit val catsDataNewInstancesForNonEmptyChain: Bimonad[NonEmptyChain] with NonEmptyTraverse[NonEmptyChain] = + new AbstractNonEmptyBimonadTraverse[Chain, NonEmptyChain] { + + def extract[A](fa: NonEmptyChain[A]): A = fa.head + + def nonEmptyTraverse[G[_]: Apply, A, B](fa: NonEmptyChain[A])(f: A => G[B]): G[NonEmptyChain[B]] = + Foldable[Chain] + .reduceRightToOption[A, G[Chain[B]]](fa.tail)(a => Apply[G].map(f(a))(Chain.one)) { (a, lglb) => + Apply[G].map2Eval(f(a), lglb)(_ +: _) + } + .map { + case None => Apply[G].map(f(fa.head))(NonEmptyChain.one) + case Some(gtail) => Apply[G].map2(f(fa.head), gtail)((h, t) => create(Chain.one(h) ++ t)) + } + .value + + def reduceLeftTo[A, B](fa: NonEmptyChain[A])(f: A => B)(g: (B, A) => B): B = fa.reduceLeftTo(f)(g) + + def reduceRightTo[A, B](fa: NonEmptyChain[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)) + }) + } + + implicit def catsDataOrderForNonEmptyChain[A: Order]: Order[NonEmptyChain[A]] = + Order[Chain[A]].asInstanceOf[Order[NonEmptyChain[A]]] + + implicit def catsDataSemigroupForNonEmptyChain[A]: Semigroup[NonEmptyChain[A]] = + Semigroup[Chain[A]].asInstanceOf[Semigroup[NonEmptyChain[A]]] + + implicit def catsDataShowForNonEmptyChain[A](implicit A: Show[A]): Show[NonEmptyChain[A]] = + Show.show[NonEmptyChain[A]](nec => s"NonEmpty${Show[Chain[A]].show(nec.toChain)}") + + @deprecated("2.0.0-RC1", "replaced by catsDataNewInstancesForNonEmptyChain, this is kept for BC") + val catsDataInstancesForNonEmptyChain : SemigroupK[NonEmptyChain] with NonEmptyTraverse[NonEmptyChain] with Bimonad[NonEmptyChain] = new SemigroupK[NonEmptyChain] with NonEmptyTraverse[NonEmptyChain] with Bimonad[NonEmptyChain] { @@ -418,29 +439,15 @@ sealed abstract private[data] class NonEmptyChainInstances extends NonEmptyChain fa.flatMap(f) def tailRecM[A, B](a: A)(f: A => NonEmptyChain[Either[A, B]]): NonEmptyChain[B] = - create(Monad[Chain].tailRecM(a)(a => unwrap(f(a)))) + catsDataNewInstancesForNonEmptyChain.tailRecM(a)(f) def extract[A](x: NonEmptyChain[A]): A = x.head - def coflatMap[A, B](fa: NonEmptyChain[A])(f: NonEmptyChain[A] => B): NonEmptyChain[B] = { - @tailrec def go(as: Chain[A], res: ListBuffer[B]): Chain[B] = - as.uncons match { - case Some((h, t)) => go(t, res += f(NonEmptyChain.fromChainPrepend(h, t))) - case None => Chain.fromSeq(res.result()) - } - NonEmptyChain.fromChainPrepend(f(fa), go(fa.tail, ListBuffer.empty)) - } + def coflatMap[A, B](fa: NonEmptyChain[A])(f: NonEmptyChain[A] => B): NonEmptyChain[B] = + catsDataNewInstancesForNonEmptyChain.coflatMap(fa)(f) def nonEmptyTraverse[G[_]: Apply, A, B](fa: NonEmptyChain[A])(f: A => G[B]): G[NonEmptyChain[B]] = - Foldable[Chain] - .reduceRightToOption[A, G[Chain[B]]](fa.tail)(a => Apply[G].map(f(a))(Chain.one)) { (a, lglb) => - Apply[G].map2Eval(f(a), lglb)(_ +: _) - } - .map { - case None => Apply[G].map(f(fa.head))(NonEmptyChain.one) - case Some(gtail) => Apply[G].map2(f(fa.head), gtail)((h, t) => create(Chain.one(h) ++ t)) - } - .value + catsDataNewInstancesForNonEmptyChain.nonEmptyTraverse(fa)(f) override def map[A, B](fa: NonEmptyChain[A])(f: A => B): NonEmptyChain[B] = create(fa.toChain.map(f)) @@ -456,9 +463,7 @@ sealed abstract private[data] class NonEmptyChainInstances extends NonEmptyChain def reduceLeftTo[A, B](fa: NonEmptyChain[A])(f: A => B)(g: (B, A) => B): B = fa.reduceLeftTo(f)(g) def reduceRightTo[A, B](fa: NonEmptyChain[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)) - }) + catsDataNewInstancesForNonEmptyChain.reduceRightTo(fa)(f)(g) override def foldLeft[A, B](fa: NonEmptyChain[A], b: B)(f: (B, A) => B): B = fa.foldLeft(b)(f) @@ -492,26 +497,23 @@ sealed abstract private[data] class NonEmptyChainInstances extends NonEmptyChain override def collectFirstSome[A, B](fa: NonEmptyChain[A])(f: A => Option[B]): Option[B] = fa.collectFirstSome(f) } +} - implicit def catsDataOrderForNonEmptyChain[A: Order]: Order[NonEmptyChain[A]] = - Order.by[NonEmptyChain[A], Chain[A]](_.toChain) +sealed abstract private[data] class NonEmptyChainInstances1 extends NonEmptyChainInstances2 { + implicit val catsDataSemigroupKForNonEmptyChain: SemigroupK[NonEmptyChain] = + SemigroupK[Chain].asInstanceOf[SemigroupK[NonEmptyChain]] - implicit def catsDataShowForNonEmptyChain[A](implicit A: Show[A]): Show[NonEmptyChain[A]] = - Show.show[NonEmptyChain[A]](nec => s"NonEmpty${Show[Chain[A]].show(nec.toChain)}") + implicit def catsDataHashForNonEmptyChain[A: Hash]: Hash[NonEmptyChain[A]] = + Hash[Chain[A]].asInstanceOf[Hash[NonEmptyChain[A]]] - implicit def catsDataSemigroupForNonEmptyChain[A]: Semigroup[NonEmptyChain[A]] = new Semigroup[NonEmptyChain[A]] { - def combine(x: NonEmptyChain[A], y: NonEmptyChain[A]): NonEmptyChain[A] = x ++ y - } } -sealed abstract private[data] class NonEmptyChainInstances1 extends NonEmptyChainInstances2 { +sealed abstract private[data] class NonEmptyChainInstances2 extends NonEmptyChainInstances3 { implicit def catsDataPartialOrderForNonEmptyChain[A: PartialOrder]: PartialOrder[NonEmptyChain[A]] = - PartialOrder.by[NonEmptyChain[A], Chain[A]](_.toChain) + PartialOrder[Chain[A]].asInstanceOf[PartialOrder[NonEmptyChain[A]]] } -sealed abstract private[data] class NonEmptyChainInstances2 { +sealed abstract private[data] class NonEmptyChainInstances3 { implicit def catsDataEqForNonEmptyChain[A: Eq]: Eq[NonEmptyChain[A]] = - new Eq[NonEmptyChain[A]] { - def eqv(x: NonEmptyChain[A], y: NonEmptyChain[A]): Boolean = x.toChain === y.toChain - } + Eq[Chain[A]].asInstanceOf[Eq[NonEmptyChain[A]]] } diff --git a/core/src/main/scala/cats/data/NonEmptySet.scala b/core/src/main/scala/cats/data/NonEmptySet.scala index e5961907d6..1baa207d3a 100644 --- a/core/src/main/scala/cats/data/NonEmptySet.scala +++ b/core/src/main/scala/cats/data/NonEmptySet.scala @@ -41,6 +41,7 @@ private[data] object NonEmptySetImpl extends NonEmptySetInstances with Newtype { def of[A](a: A, as: A*)(implicit A: Order[A]): NonEmptySet[A] = create(SortedSet(a +: as: _*)(A.toOrdering)) + def apply[A](head: A, tail: SortedSet[A])(implicit A: Order[A]): NonEmptySet[A] = create(SortedSet(head)(A.toOrdering) ++ tail) def one[A](a: A)(implicit A: Order[A]): NonEmptySet[A] = create(SortedSet(a)(A.toOrdering)) diff --git a/core/src/main/scala/cats/data/package.scala b/core/src/main/scala/cats/data/package.scala index 2832468de0..30619dcdfb 100644 --- a/core/src/main/scala/cats/data/package.scala +++ b/core/src/main/scala/cats/data/package.scala @@ -2,7 +2,7 @@ package cats import kernel.compat.scalaVersionSpecific._ import compat.lazyList.toLazyList -package object data { +package object data extends ScalaVersionSpecificPackage { type NonEmptyStream[A] = OneAnd[LazyList, A] type ValidatedNel[+E, +A] = Validated[NonEmptyList[E], A] diff --git a/kernel/src/main/scala-2.13+/cats/kernel/compat/scalaVersionMoreSpecific.scala b/kernel/src/main/scala-2.13+/cats/kernel/compat/scalaVersionMoreSpecific.scala index f8b39f6853..5f8c6aec2a 100644 --- a/kernel/src/main/scala-2.13+/cats/kernel/compat/scalaVersionMoreSpecific.scala +++ b/kernel/src/main/scala-2.13+/cats/kernel/compat/scalaVersionMoreSpecific.scala @@ -2,6 +2,7 @@ package cats.kernel.compat import scala.annotation.{Annotation, StaticAnnotation} private[cats] object scalaVersionMoreSpecific { + /** * a trick to suppress unused import warning for this object */ diff --git a/kernel/src/main/scala/cats/kernel/instances/StreamInstances.scala b/kernel/src/main/scala/cats/kernel/instances/StreamInstances.scala index dcdde43a8e..e8efde0b10 100644 --- a/kernel/src/main/scala/cats/kernel/instances/StreamInstances.scala +++ b/kernel/src/main/scala/cats/kernel/instances/StreamInstances.scala @@ -1,6 +1,7 @@ package cats.kernel package instances import compat.scalaVersionSpecific._ + @suppressUnusedImportWarningForScalaVersionSpecific trait StreamInstances extends StreamInstances1 { implicit def catsKernelStdOrderForStream[A: Order]: Order[LazyList[A]] = diff --git a/laws/src/main/scala-2.12-/cats/laws/discipline/ScalaVersionSpecific.scala b/laws/src/main/scala-2.12-/cats/laws/discipline/ScalaVersionSpecific.scala new file mode 100644 index 0000000000..09bd2d0daa --- /dev/null +++ b/laws/src/main/scala-2.12-/cats/laws/discipline/ScalaVersionSpecific.scala @@ -0,0 +1,7 @@ +package cats +package laws +package discipline + +private[discipline] object ScalaVersionSpecific { + trait ArbitraryInstances +} diff --git a/laws/src/main/scala-2.13+/cats/laws/discipline/ScalaVersionSpecific.scala b/laws/src/main/scala-2.13+/cats/laws/discipline/ScalaVersionSpecific.scala new file mode 100644 index 0000000000..26eaa00240 --- /dev/null +++ b/laws/src/main/scala-2.13+/cats/laws/discipline/ScalaVersionSpecific.scala @@ -0,0 +1,19 @@ +package cats.laws.discipline + +import cats.data.NonEmptyLazyList +import org.scalacheck.{Arbitrary, Cogen} + +private[discipline] object ScalaVersionSpecific { + + trait ArbitraryInstances { + + implicit def catsLawsArbitraryForNonEmptyLazyList[A](implicit A: Arbitrary[A]): Arbitrary[NonEmptyLazyList[A]] = + Arbitrary( + implicitly[Arbitrary[LazyList[A]]].arbitrary + .flatMap(fa => A.arbitrary.map(a => NonEmptyLazyList.fromLazyListPrepend(a, fa))) + ) + implicit def catsLawsCogenForNonEmptyLazyList[A](implicit A: Cogen[A]): Cogen[NonEmptyLazyList[A]] = + Cogen[LazyList[A]].contramap(_.toLazyList) + + } +} diff --git a/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala b/laws/src/main/scala/cats/laws/discipline/arbitrary.scala similarity index 99% rename from laws/src/main/scala/cats/laws/discipline/Arbitrary.scala rename to laws/src/main/scala/cats/laws/discipline/arbitrary.scala index d805e72ad6..1772fec0a4 100644 --- a/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala +++ b/laws/src/main/scala/cats/laws/discipline/arbitrary.scala @@ -4,6 +4,7 @@ package discipline import kernel.compat.scalaVersionSpecific._ import cats.data.NonEmptyList.ZipNonEmptyList import cats.data.NonEmptyVector.ZipNonEmptyVector + import scala.util.{Failure, Success, Try} import scala.collection.immutable.{SortedMap, SortedSet} import cats.data._ @@ -14,7 +15,7 @@ import org.scalacheck.Arbitrary.{arbitrary => getArbitrary} * Arbitrary instances for cats.data */ @suppressUnusedImportWarningForScalaVersionSpecific -object arbitrary extends ArbitraryInstances0 { +object arbitrary extends ArbitraryInstances0 with ScalaVersionSpecific.ArbitraryInstances { // this instance is not available in ScalaCheck 1.13.2. // remove this once a newer version is available. diff --git a/tests/src/test/scala-2.12-/cats/tests/LegacyNonEmptyVectorSuite.scala b/tests/src/test/scala-2.12-/cats/tests/LegacyNonEmptyVectorSuite.scala new file mode 100644 index 0000000000..505819aa8d --- /dev/null +++ b/tests/src/test/scala-2.12-/cats/tests/LegacyNonEmptyVectorSuite.scala @@ -0,0 +1,23 @@ +package cats +package tests + +import cats.data.NonEmptyVector + +import cats.laws.discipline.arbitrary._ + +/** + * The toString cannot be overriden by the new type encoding + */ +class LegacyNonEmptyVectorSuite extends CatsSuite { + implicit override val generatorDrivenConfig: PropertyCheckConfiguration = + PropertyCheckConfiguration(minSuccessful = 20, sizeRange = 5) + + test("NonEmptyVector#toString produces correct output") { + forAll { (nonEmptyVector: NonEmptyVector[Int]) => + nonEmptyVector.toString should ===(s"NonEmpty${nonEmptyVector.toVector.toString}") + } + NonEmptyVector(1, Vector.empty).toString should ===("NonEmptyVector(1)") + NonEmptyVector(1, Vector.empty).toVector.toString should ===("Vector(1)") + } + +} 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..33774472eb --- /dev/null +++ b/tests/src/test/scala-2.12-/cats/tests/NonEmptyStreamSuite.scala @@ -0,0 +1,169 @@ +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-2.13+/cats/tests/NonEmptyLazyListSuite.scala b/tests/src/test/scala-2.13+/cats/tests/NonEmptyLazyListSuite.scala new file mode 100644 index 0000000000..dbe8da1f3a --- /dev/null +++ b/tests/src/test/scala-2.13+/cats/tests/NonEmptyLazyListSuite.scala @@ -0,0 +1,141 @@ +package cats +package tests + +import cats.data.NonEmptyLazyList +import cats.kernel.laws.discipline.{EqTests, HashTests, OrderTests, PartialOrderTests, SemigroupTests} +import cats.laws.discipline.{BimonadTests, NonEmptyTraverseTests, SemigroupKTests, SerializableTests} +import cats.laws.discipline.arbitrary._ + +class NonEmptyLazyListSuite extends CatsSuite { + + checkAll("NonEmptyLazyList[Int]", SemigroupTests[NonEmptyLazyList[Int]].semigroup) + checkAll(s"Semigroup[NonEmptyLazyList]", SerializableTests.serializable(Semigroup[NonEmptyLazyList[Int]])) + + checkAll(s"NonEmptyLazyList[Int]", HashTests[NonEmptyLazyList[Int]].hash) + checkAll(s"Hash[NonEmptyLazyList[Int]]", SerializableTests.serializable(Hash[NonEmptyLazyList[Int]])) + + checkAll("NonEmptyLazyList[Int]", SemigroupKTests[NonEmptyLazyList].semigroupK[Int]) + checkAll("SemigroupK[NonEmptyLazyList]", SerializableTests.serializable(SemigroupK[NonEmptyLazyList])) + + checkAll("NonEmptyLazyList[Int] with Option", + NonEmptyTraverseTests[NonEmptyLazyList].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option]) + checkAll("NonEmptyTraverse[NonEmptyLazyList]", SerializableTests.serializable(Traverse[NonEmptyLazyList])) + + checkAll("NonEmptyLazyList[Int]", BimonadTests[NonEmptyLazyList].bimonad[Int, Int, Int]) + checkAll("Bimonad[NonEmptyLazyList]", SerializableTests.serializable(Bimonad[NonEmptyLazyList])) + + checkAll("NonEmptyLazyList[Int]", OrderTests[NonEmptyLazyList[Int]].order) + checkAll("Order[NonEmptyLazyList[Int]", SerializableTests.serializable(Order[NonEmptyLazyList[Int]])) + + test("show") { + Show[NonEmptyLazyList[Int]].show(NonEmptyLazyList(1, 2, 3)) should ===("NonEmptyLazyList(1, ?)") + } + checkAll("Show[NonEmptyLazyList[Int]]", SerializableTests.serializable(Show[NonEmptyLazyList[Int]])) + + { + implicit val partialOrder = ListWrapper.partialOrder[Int] + checkAll("NonEmptyLazyList[ListWrapper[Int]]", PartialOrderTests[NonEmptyLazyList[ListWrapper[Int]]].partialOrder) + checkAll("PartialOrder[NonEmptyLazyList[ListWrapper[Int]]", + SerializableTests.serializable(PartialOrder[NonEmptyLazyList[ListWrapper[Int]]])) + } + + { + implicit val eqv = ListWrapper.eqv[Int] + checkAll("NonEmptyLazyList[ListWrapper[Int]]", EqTests[NonEmptyLazyList[ListWrapper[Int]]].eqv) + checkAll("Eq[NonEmptyLazyList[ListWrapper[Int]]", + SerializableTests.serializable(Eq[NonEmptyLazyList[ListWrapper[Int]]])) + } + + test("size is consistent with toLazyList.size") { + forAll { (ci: NonEmptyLazyList[Int]) => + ci.size should ===(ci.toLazyList.size.toLong) + } + } + + test("filterNot and then exists should always be false") { + forAll { (ci: NonEmptyLazyList[Int], f: Int => Boolean) => + ci.filterNot(f).exists(f) should ===(false) + } + } + + test("filter and then forall should always be true") { + forAll { (ci: NonEmptyLazyList[Int], f: Int => Boolean) => + ci.filter(f).forall(f) should ===(true) + } + } + + test("exists should be consistent with find + isDefined") { + forAll { (ci: NonEmptyLazyList[Int], f: Int => Boolean) => + ci.exists(f) should ===(ci.find(f).isDefined) + } + } + + test("filterNot element and then contains should be false") { + forAll { (ci: NonEmptyLazyList[Int], i: Int) => + ci.filterNot(_ === i).contains(i) should ===(false) + } + } + + test("fromNonEmptyVector . toNonEmptyVector is id") { + forAll { (ci: NonEmptyLazyList[Int]) => + NonEmptyLazyList.fromNonEmptyVector(ci.toNonEmptyVector) should ===(ci) + } + } + + test("fromNonEmptyList . toNonEmptyList is id") { + forAll { (ci: NonEmptyLazyList[Int]) => + NonEmptyLazyList.fromNonEmptyList(ci.toNonEmptyList) should ===(ci) + } + } + + test("fromLazyList . toLazyList is Option.some") { + forAll { (ci: NonEmptyLazyList[Int]) => + NonEmptyLazyList.fromLazyList(ci.toLazyList) should ===(Some(ci)) + } + } + + test("fromLazyListUnsafe throws exception when used with empty LazyList") { + Either.catchNonFatal(NonEmptyLazyList.fromLazyListUnsafe(LazyList.empty[Int])).isLeft should ===(true) + } + + test("fromSeq . toList . iterator is id") { + forAll { (ci: NonEmptyLazyList[Int]) => + NonEmptyLazyList.fromSeq(ci.iterator.toList) should ===(Option(ci)) + } + } + + test("zipWith consistent with List#zip and then List#map") { + forAll { (a: NonEmptyLazyList[String], b: NonEmptyLazyList[Int], f: (String, Int) => Int) => + a.zipWith(b)(f).toList should ===(a.toList.zip(b.toList).map { case (x, y) => f(x, y) }) + } + } + + test("reverse . reverse is id") { + forAll { (ci: NonEmptyLazyList[Int]) => + ci.reverse.reverse should ===(ci) + } + } + + test("reverse consistent with LazyList#reverse") { + forAll { (ci: NonEmptyLazyList[Int]) => + ci.reverse.toLazyList should ===(ci.toLazyList.reverse) + } + } + + test("NonEmptyLazyList#distinct is consistent with List#distinct") { + forAll { ci: NonEmptyLazyList[Int] => + ci.distinct.toList should ===(ci.toList.distinct) + } + } +} + +class ReducibleNonEmptyLazyListSuite extends ReducibleSuite[NonEmptyLazyList]("NonEmptyLazyList") { + def iterator[T](nel: NonEmptyLazyList[T]): Iterator[T] = nel.toLazyList.iterator + + 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 + NonEmptyLazyList(start, (tailStart).to(endInclusive): _*) + } +} diff --git a/tests/src/test/scala/cats/tests/NonEmptyChainSuite.scala b/tests/src/test/scala/cats/tests/NonEmptyChainSuite.scala index b9939ee25c..bc986ffc05 100644 --- a/tests/src/test/scala/cats/tests/NonEmptyChainSuite.scala +++ b/tests/src/test/scala/cats/tests/NonEmptyChainSuite.scala @@ -140,3 +140,14 @@ class NonEmptyChainSuite extends CatsSuite { } } } + +class ReducibleNonEmptyChainSuite extends ReducibleSuite[NonEmptyChain]("NonEmptyChain") { + def iterator[T](nel: NonEmptyChain[T]): Iterator[T] = nel.toChain.iterator + + def range(start: Long, endInclusive: Long): NonEmptyChain[Long] = { + // if we inline this we get a bewildering implicit numeric widening + // error message in Scala 2.10 + val tailStart: Long = start + 1L + NonEmptyChain(start, (tailStart).to(endInclusive): _*) + } +} diff --git a/tests/src/test/scala/cats/tests/NonEmptyListSuite.scala b/tests/src/test/scala/cats/tests/NonEmptyListSuite.scala index b26c6ba674..665c2038fc 100644 --- a/tests/src/test/scala/cats/tests/NonEmptyListSuite.scala +++ b/tests/src/test/scala/cats/tests/NonEmptyListSuite.scala @@ -275,7 +275,7 @@ class NonEmptyListSuite extends CatsSuite { test("NonEmptyList#size and length is consistent with List#size") { forAll { nel: NonEmptyList[Int] => - nel.size should ===(nel.toList.size) + nel.size.toLong should ===(nel.toList.size.toLong) nel.length should ===(nel.toList.size) } } @@ -346,16 +346,6 @@ class NonEmptyListSuite extends CatsSuite { } } -@deprecated("to be able to test deprecated methods", since = "1.0.0-RC1") -class DeprecatedNonEmptyListSuite extends CatsSuite { - - test("Deprecated NonEmptyList#concat is consistent with List#:::") { - forAll { (nel: NonEmptyList[Int], l: List[Int], n: Int) => - nel.concat(NonEmptyList(n, l)).toList should ===(nel.toList ::: (n :: l)) - } - } -} - class ReducibleNonEmptyListSuite extends ReducibleSuite[NonEmptyList]("NonEmptyList") { def iterator[T](nel: NonEmptyList[T]): Iterator[T] = nel.toList.iterator diff --git a/tests/src/test/scala/cats/tests/NonEmptyVectorSuite.scala b/tests/src/test/scala/cats/tests/NonEmptyVectorSuite.scala index 226c809dd0..eb34703705 100644 --- a/tests/src/test/scala/cats/tests/NonEmptyVectorSuite.scala +++ b/tests/src/test/scala/cats/tests/NonEmptyVectorSuite.scala @@ -272,14 +272,6 @@ class NonEmptyVectorSuite extends CatsSuite { } } - test("NonEmptyVector#toString produces correct output") { - forAll { (nonEmptyVector: NonEmptyVector[Int]) => - nonEmptyVector.toString should ===(s"NonEmpty${nonEmptyVector.toVector.toString}") - } - NonEmptyVector(1, Vector.empty).toString should ===("NonEmptyVector(1)") - NonEmptyVector(1, Vector.empty).toVector.toString should ===("Vector(1)") - } - test("NonEmptyVector.unapply supports pattern matching") { forAll { (nonEmptyVector: NonEmptyVector[Int]) => nonEmptyVector match { diff --git a/tests/src/test/scala/cats/tests/OneAndSuite.scala b/tests/src/test/scala/cats/tests/OneAndSuite.scala index 401fca5ba9..62a6c03aa1 100644 --- a/tests/src/test/scala/cats/tests/OneAndSuite.scala +++ b/tests/src/test/scala/cats/tests/OneAndSuite.scala @@ -1,39 +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.scalaVersionSpecific._ -import compat.lazyList.toLazyList -@suppressUnusedImportWarningForScalaVersionSpecific 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) checkAll("OneAnd[ListWrapper, Int] with Option", @@ -41,9 +17,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)) @@ -70,9 +43,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]])) } { @@ -81,160 +52,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 - nel.unwrap.toList.reverse match { - case last :: rev => - val expected = rev.reverse.foldRight(last)((a, b) => f(a, Now(b)).value) - got should ===(expected) - case _ => fail("nonempty turns out to be empty") - } - - } - } - - 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 - nel.unwrap.toList.reverse match { - case last :: rev => - val expected = rev.reverse.foldRight(Option(f(last))) { (i, opt) => - opt.map(s => g(i, Now(s)).value) - } - got should ===(expected) - case _ => fail("nonempty turns out to be empty") - } - } - } - - 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))) - } } diff --git a/tests/src/test/scala/cats/tests/ParallelSuite.scala b/tests/src/test/scala/cats/tests/ParallelSuite.scala index 16a7a2b020..a755c510a6 100644 --- a/tests/src/test/scala/cats/tests/ParallelSuite.scala +++ b/tests/src/test/scala/cats/tests/ParallelSuite.scala @@ -447,10 +447,13 @@ class ParallelSuite extends CatsSuite with ApplicativeErrorForEitherTest { checkAll("NonEmptyParallel[List, ZipList]", NonEmptyParallelTests[List, ZipList].nonEmptyParallel[Int, String]) // Can't test Parallel here, as Applicative[ZipStream].pure doesn't terminate checkAll("Parallel[Stream, ZipStream]", NonEmptyParallelTests[LazyList, ZipStream].nonEmptyParallel[Int, String]) + checkAll("NonEmptyParallel[NonEmptyVector, ZipNonEmptyVector]", NonEmptyParallelTests[NonEmptyVector, ZipNonEmptyVector].nonEmptyParallel[Int, String]) + checkAll("NonEmptyParallel[NonEmptyList, ZipNonEmptyList]", NonEmptyParallelTests[NonEmptyList, ZipNonEmptyList].nonEmptyParallel[Int, String]) + checkAll("Parallel[NonEmptyStream, OneAnd[ZipStream, *]", ParallelTests[NonEmptyStream, OneAnd[ZipStream, *]].parallel[Int, String])