diff --git a/core/src/main/scala-2.13+/cats/instances/all.scala b/core/src/main/scala-2.13/cats/instances/all.scala similarity index 100% rename from core/src/main/scala-2.13+/cats/instances/all.scala rename to core/src/main/scala-2.13/cats/instances/all.scala diff --git a/core/src/main/scala-2.13+/cats/instances/package.scala b/core/src/main/scala-2.13/cats/instances/package.scala similarity index 100% rename from core/src/main/scala-2.13+/cats/instances/package.scala rename to core/src/main/scala-2.13/cats/instances/package.scala diff --git a/core/src/main/scala-3/cats/instances/all.scala b/core/src/main/scala-3/cats/instances/all.scala new file mode 100644 index 0000000000..3343eb3539 --- /dev/null +++ b/core/src/main/scala-3/cats/instances/all.scala @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2015 Typelevel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package cats +package instances + +abstract class AllInstancesBinCompat + extends AllInstances + with AllInstancesBinCompat0 + with AllInstancesBinCompat1 + with AllInstancesBinCompat2 + with AllInstancesBinCompat3 + with AllInstancesBinCompat4 + with AllInstancesBinCompat5 + with AllInstancesBinCompat6 + with AllInstancesBinCompat7 + with AllInstancesBinCompat8 + with AllInstancesBinCompat9 + +trait AllInstances + extends AnyValInstances + with ArraySeqInstances + with BigIntInstances + with BigDecimalInstances + with BitSetInstances + with EitherInstances + with EqInstances + with EquivInstances + with FunctionInstances + with FutureInstances + with HashInstances + with InvariantMonoidalInstances + with LazyListInstances + with ListInstances + with MapInstances + with OptionInstances + with OrderInstances + with OrderingInstances + with ParallelInstances + with PartialOrderInstances + with PartialOrderingInstances + with QueueInstances + with SetInstances + with SortedMapInstances + with SortedSetInstances + with ShowInstances + with StreamInstances + with StringInstances + with SymbolInstances + with TailRecInstances + with TryInstances + with TupleInstances + with UUIDInstances + with VectorInstances + with PartialFunctionInstances + with IArrayInstances + +trait AllInstancesBinCompat0 extends FunctionInstancesBinCompat0 with Tuple2InstancesBinCompat0 + +trait AllInstancesBinCompat1 + extends OptionInstancesBinCompat0 + with ListInstancesBinCompat0 + with VectorInstancesBinCompat0 + with StreamInstancesBinCompat0 + with MapInstancesBinCompat0 + with SortedMapInstancesBinCompat0 + +trait AllInstancesBinCompat2 extends DurationInstances with FiniteDurationInstances + +trait AllInstancesBinCompat3 extends AllCoreDurationInstances + +trait AllInstancesBinCompat4 extends SortedMapInstancesBinCompat1 with MapInstancesBinCompat1 + +trait AllInstancesBinCompat5 extends SortedSetInstancesBinCompat0 + +trait AllInstancesBinCompat6 extends SortedSetInstancesBinCompat1 with SortedMapInstancesBinCompat2 + +trait AllInstancesBinCompat7 extends SeqInstances + +trait AllInstancesBinCompat8 extends InvariantInstances + +trait AllInstancesBinCompat9 extends DeadlineInstances + +trait AllInstancesBinCompat10 extends InvariantInstancesBinCompat0 diff --git a/core/src/main/scala-3/cats/instances/iarray.scala b/core/src/main/scala-3/cats/instances/iarray.scala new file mode 100644 index 0000000000..f55cbb4aec --- /dev/null +++ b/core/src/main/scala-3/cats/instances/iarray.scala @@ -0,0 +1,348 @@ +/* + * Copyright (c) 2015 Typelevel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package cats +package instances + +import cats.data.{Chain, Ior} +import cats.instances.StaticMethods.{appendAll, appendAllMixed} +import cats.kernel.compat.scalaVersionSpecific.* + +import scala.annotation.tailrec +import scala.reflect.ClassTag +import scala.collection.mutable.Builder + +trait IArrayInstances extends cats.kernel.instances.IArrayInstances { + implicit val catsStdInstancesForIArray + : Traverse[IArray] & Monad[IArray] & Alternative[IArray] & CoflatMap[IArray] & Align[IArray] = + new Traverse[IArray] with Monad[IArray] with Alternative[IArray] with CoflatMap[IArray] with Align[IArray] { + + def empty[A]: IArray[A] = { + implicit val fakeClassTag: ClassTag[A] = summonFakeTag[A] + IArray.empty[A] + } + + def combineK[A](x: IArray[A], y: IArray[A]): IArray[A] = { + implicit val fakeClassTag: ClassTag[A] = summonFakeTag[A] + x.appendedAll(y) + } + + override def combineAllOptionK[A](as: IterableOnce[IArray[A]]): Option[IArray[A]] = { + implicit val fakeClassTag: ClassTag[A] = summonFakeTag[A] + val iter = as.iterator + if iter.isEmpty then None else Some(appendAllMixed(iter.map(_.iterator), IArray.newBuilder[A]).result()) + } + + override def fromIterableOnce[A](as: IterableOnce[A]): IArray[A] = { + implicit val fakeClassTag: ClassTag[A] = summonFakeTag[A] + IArray.unsafeFromArray(as.iterator.toArray) + } + + override def prependK[A](a: A, fa: IArray[A]): IArray[A] = { + implicit val fakeClassTag: ClassTag[A] = summonFakeTag[A] + fa.prepended(a) + } + + override def appendK[A](fa: IArray[A], a: A): IArray[A] = { + implicit val fakeClassTag: ClassTag[A] = summonFakeTag[A] + fa.appended(a) + } + + def pure[A](x: A): IArray[A] = { + implicit val fakeClassTag: ClassTag[A] = summonFakeTag[A] + IArray(x) + } + + + + override def map[A, B](fa: IArray[A])(f: A => B): IArray[B] = { + implicit val fakeClassTag: ClassTag[B] = ClassTag.Any.asInstanceOf[ClassTag[B]] + fa.map(f) + } + + def flatMap[A, B](fa: IArray[A])(f: A => IArray[B]): IArray[B] = { + implicit val fakeClassTag: ClassTag[B] = ClassTag.Any.asInstanceOf[ClassTag[B]] + fa.flatMap(f.andThen(_.iterator)) + } + + override def map2[A, B, Z](fa: IArray[A], fb: IArray[B])(f: (A, B) => Z): IArray[Z] = { + implicit val fakeClassTag: ClassTag[Z] = summonFakeTag[Z] + if (fb.isEmpty) IArray.empty[Z] // do O(1) work if either is empty + else fa.flatMap(a => fb.map(b => f(a, b))) // already O(1) if fa is empty + } + + private[this] val evalEmpty: Eval[IArray[Nothing]] = Eval.now(IArray.empty(ClassTag.Nothing)) + private[this] def summonFakeTag[T]: ClassTag[T] = ClassTag.Any.asInstanceOf[ClassTag[T]] + + override def map2Eval[A, B, Z](fa: IArray[A], fb: Eval[IArray[B]])(f: (A, B) => Z): Eval[IArray[Z]] = + if fa.isEmpty then evalEmpty // no need to evaluate fb + else fb.map(fb => map2(fa, fb)(f)) + + def coflatMap[A, B](fa: IArray[A])(f: IArray[A] => B): IArray[B] = { + implicit val fakeClassTag: ClassTag[B] = summonFakeTag[B] + @tailrec def loop(builder: Builder[B, IArray[B]], as: IArray[A]): IArray[B] = { + if as.nonEmpty then + loop(builder += f(as), as.tail) + else + builder.result() + } + + loop(IArray.newBuilder[B], fa) + } + + def foldLeft[A, B](fa: IArray[A], b: B)(f: (B, A) => B): B = + fa.foldLeft(b)(f) + + def foldRight[A, B](fa: IArray[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = { + def loop(i: Int): Eval[B] = + if i < fa.length then f(fa(i), Eval.defer(loop(i + 1))) else lb + Eval.defer(loop(0)) + } + + override def foldMap[A, B](fa: IArray[A])(f: A => B)(implicit B: Monoid[B]): B = + B.combineAll(fa.iterator.map(f)) + + def tailRecM[A, B](a: A)(fn: A => IArray[Either[A, B]]): IArray[B] = { + implicit val fakeClassTag: ClassTag[B] = ClassTag.Any.asInstanceOf[ClassTag[B]] + val buf = IArray.newBuilder[B] + var state = List(fn(a).iterator) + @tailrec + def loop(): Unit = + state match { + case Nil => () + case h :: tail if h.isEmpty => + state = tail + loop() + case h :: tail => + h.next() match { + case Right(b) => + buf += b + loop() + case Left(a) => + state = (fn(a).iterator) :: h :: tail + loop() + } + } + loop() + buf.result() + } + + override def size[A](fa: IArray[A]): Long = fa.size.toLong + + override def get[A](fa: IArray[A])(idx: Long): Option[A] = + if idx < Int.MaxValue && fa.size > idx && idx >= 0 then Some(fa(idx.toInt)) else None + + override def foldMapK[G[_], A, B](fa: IArray[A])(f: A => G[B])(implicit G: MonoidK[G]): G[B] = { + def loop(i: Int): Eval[G[B]] = + if i < fa.length then G.combineKEval(f(fa(i)), Eval.defer(loop(i + 1))) else Eval.now(G.empty) + loop(0).value + } + + final override def traverse[G[_], A, B](fa: IArray[A])(f: A => G[B])(implicit G: Applicative[G]): G[IArray[B]] = { + implicit val fakeClassTag: ClassTag[B] = ClassTag.Any.asInstanceOf[ClassTag[B]] + G match { + case x: StackSafeMonad[G] => + x.map(Traverse.traverseDirectly(fa)(f)(x))(it => IArray.unsafeFromArray(it.toVector.toArray)) + case _ => G.map(Chain.traverseViaChain(fa)(f))(it => IArray.unsafeFromArray(it.toVector.toArray)) + } + } + + final override def updated_[A, B >: A](fa: IArray[A], idx: Long, b: B): Option[IArray[B]] = { + implicit val fakeClassTag: ClassTag[B] = summonFakeTag[B] + if idx >= 0L && idx < fa.size.toLong then { + Some(fa.updated(idx.toInt, b)) + } else { + None + } + } + + /** + * This avoids making a very deep stack by building a tree instead + */ + override def traverseVoid[G[_], A, B](fa: IArray[A])(f: A => G[B])(implicit G: Applicative[G]): G[Unit] = { + implicit val fakeClassTag: ClassTag[B] = ClassTag.Any.asInstanceOf[ClassTag[B]] + G match { + case x: StackSafeMonad[G] => Traverse.traverseVoidDirectly(fa)(f)(x) + case _ => + // the cost of this is O(size) + // c(n) = 1 + 2 * c(n/2) + // invariant: size >= 1 + def runHalf(size: Int, idx: Int): Eval[G[Unit]] = + if size > 1 then { + val leftSize = size / 2 + val rightSize = size - leftSize + runHalf(leftSize, idx) + .flatMap { left => + val right = runHalf(rightSize, idx + leftSize) + G.map2Eval(left, right) { (_, _) => () } + } + } else { + val a = fa(idx) + // we evaluate this at most one time, + // always is a bit cheaper in such cases + // + // Here is the point of the laziness using Eval: + // we avoid calling f(a) or G.void in the + // event that the computation has already + // failed. We do not use laziness to avoid + // traversing fa, which we will do fully + // in all cases. + Eval.always { + val gb = f(a) + G.void(gb) + } + } + + val len = fa.length + if len == 0 then G.unit + else runHalf(len, 0).value + } + } + + override def mapAccumulate[S, A, B](init: S, fa: IArray[A])(f: (S, A) => (S, B)): (S, IArray[B]) = + StaticMethods.mapAccumulateFromStrictFunctor(init, fa, f)(this) + + override def mapWithIndex[A, B](fa: IArray[A])(f: (A, Int) => B): IArray[B] = + StaticMethods.mapWithIndexFromStrictFunctor(fa, f)(this) + + override def mapWithLongIndex[A, B](fa: IArray[A])(f: (A, Long) => B): IArray[B] = + StaticMethods.mapWithLongIndexFromStrictFunctor(fa, f)(this) + + override def zipWithIndex[A](fa: IArray[A]): IArray[(A, Int)] = + fa.zipWithIndex + + override def exists[A](fa: IArray[A])(p: A => Boolean): Boolean = + fa.exists(p) + + override def isEmpty[A](fa: IArray[A]): Boolean = + fa.isEmpty + + override def foldM[G[_], A, B](fa: IArray[A], z: B)(f: (B, A) => G[B])(implicit G: Monad[G]): G[B] = { + val length = fa.length + G.tailRecM((z, 0)) { case (b, i) => + if i < length then G.map(f(b, fa(i)))(b => Left((b, i + 1))) + else G.pure(Right(b)) + } + } + + override def fold[A](fa: IArray[A])(implicit A: Monoid[A]): A = A.combineAll(fa) + + override def toList[A](fa: IArray[A]): List[A] = fa.toList + + override def toIterable[A](fa: IArray[A]): Iterable[A] = fa + + override def reduceLeftOption[A](fa: IArray[A])(f: (A, A) => A): Option[A] = + fa.reduceLeftOption(f) + + override def find[A](fa: IArray[A])(f: A => Boolean): Option[A] = fa.find(f) + + override def algebra[A]: Monoid[IArray[A]] = { + implicit val fakeClassTag: ClassTag[A] = summonFakeTag[A] + kernel.instances.IArrayMonoid[A] + } + + def functor: Functor[IArray] = this + + def align[A, B](fa: IArray[A], fb: IArray[B]): IArray[A Ior B] = { + val aLarger = fa.size >= fb.size + val faMut = fa.asInstanceOf[Array[A]] + val fbMut = fb.asInstanceOf[Array[B]] + val prefix = faMut.lazyZip(fbMut).map(Ior.both).asInstanceOf[IArray[A Ior B]] + if aLarger then { + prefix ++ fa.drop(fb.size).map(Ior.left) + } else { + prefix ++ fb.drop(fa.size).map(Ior.right) + } + } + + override def collectFirst[A, B](fa: IArray[A])(pf: PartialFunction[A, B]): Option[B] = fa.collectFirst(pf) + + override def collectFirstSome[A, B](fa: IArray[A])(f: A => Option[B]): Option[B] = + fa.collectFirst(Function.unlift(f)) + } + + implicit def catsStdShowForIArray[A: Show]: Show[IArray[A]] = + _.iterator.map(Show[A].show).mkString("IArray(", ", ", ")") + + implicit val catsStdTraverseFilterForIArray: TraverseFilter[IArray] = new TraverseFilter[IArray] { + val traverse: Traverse[IArray] = cats.instances.iarray.catsStdInstancesForIArray + + override def mapFilter[A, B](fa: IArray[A])(f: (A) => Option[B]): IArray[B] = { + implicit val fakeClassTag: ClassTag[B] = ClassTag.Any.asInstanceOf[ClassTag[B]] + fa.collect(Function.unlift(f)) + } + + override def filter[A](fa: IArray[A])(f: (A) => Boolean): IArray[A] = fa.filter(f) + + override def filterNot[A](fa: IArray[A])(f: A => Boolean): IArray[A] = fa.filterNot(f) + + override def collect[A, B](fa: IArray[A])(f: PartialFunction[A, B]): IArray[B] = { + implicit val fakeClassTag: ClassTag[B] = ClassTag.Any.asInstanceOf[ClassTag[B]] + fa.collect(f) + } + + override def flattenOption[A](fa: IArray[Option[A]]): IArray[A] = { + implicit val fakeClassTag: ClassTag[A] = ClassTag.Any.asInstanceOf[ClassTag[A]] + IArray.flatten(fa) + } + + + + def traverseFilter[G[_], A, B]( + fa: IArray[A] + )(f: (A) => G[Option[B]])(implicit G: Applicative[G]): G[IArray[B]] = { + implicit val fakeClassTag: ClassTag[B] = ClassTag.Any.asInstanceOf[ClassTag[B]] + G match { + case x: StackSafeMonad[G] => + x.map(TraverseFilter.traverseFilterDirectly(fa)(f)(x))(it => IArray.unsafeFromArray(it.toVector.toArray)) + case _ => + G.map(Chain.traverseFilterViaChain(fa)(f))(it => IArray.unsafeFromArray(it.toVector.toArray)) + } + } + + override def filterA[G[_], A]( + fa: IArray[A] + )(f: (A) => G[Boolean])(implicit G: Applicative[G]): G[IArray[A]] = { + implicit val fakeClassTag: ClassTag[A] = ClassTag.Any.asInstanceOf[ClassTag[A]] + traverse + .foldRight(fa, Eval.now(G.pure(IArray.empty[A])))((x, xse) => + G.map2Eval(f(x), xse)((b, IArray) => if b then x +: IArray else IArray) + ) + .value + } + } + + /* + implicit def catsStdNonEmptyParallelForIArrayZipIArray: NonEmptyParallel.Aux[IArray, ZipIArray] = + new NonEmptyParallel[IArray] { + type F[x] = ZipIArray[x] + + def flatMap: FlatMap[IArray] = cats.instances.IArray.catsStdInstancesForIArray + def apply: Apply[ZipIArray] = ZipIArray.catsDataCommutativeApplyForZipIArray + + def sequential: ZipIArray ~> IArray = + new (ZipIArray ~> IArray) { def apply[A](a: ZipIArray[A]): IArray[A] = a.value } + + def parallel: IArray ~> ZipIArray = + new (IArray ~> ZipIArray) { def apply[A](v: IArray[A]): ZipIArray[A] = new ZipIArray(v) } + } + */ +} diff --git a/core/src/main/scala-3/cats/instances/package.scala b/core/src/main/scala-3/cats/instances/package.scala new file mode 100644 index 0000000000..bd2e4c726f --- /dev/null +++ b/core/src/main/scala-3/cats/instances/package.scala @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015 Typelevel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package cats + +package object instances { + object all extends AllInstancesBinCompat + object arraySeq extends ArraySeqInstances + object bigInt extends BigIntInstances + object bigDecimal extends BigDecimalInstances + object bitSet extends BitSetInstances + object boolean extends BooleanInstances + object byte extends ByteInstances + object char extends CharInstances + object double extends DoubleInstances + object duration extends CoreDurationInstances with DurationInstances + object either extends EitherInstances + object eq extends EqInstances + object equiv extends EquivInstances + object float extends FloatInstances + object finiteDuration extends CoreFiniteDurationInstances with FiniteDurationInstances + object deadline extends DeadlineInstances + object function extends FunctionInstances with FunctionInstancesBinCompat0 + object partialFunction extends PartialFunctionInstances + + /** + * @deprecated + * Any non-pure use of [[scala.concurrent.Future Future]] with Cats is error prone + * (particularly the semantics of [[cats.Traverse#traverse traverse]] with regard to execution order are unspecified). + * We recommend using [[https://typelevel.org/cats-effect/ Cats Effect `IO`]] as a replacement for ''every'' use case of [[scala.concurrent.Future Future]]. + * However, at this time there are no plans to remove these instances from Cats. + * + * @see [[https://github.com/typelevel/cats/issues/4176 Changes in Future traverse behavior between 2.6 and 2.7]] + */ + object future extends FutureInstances + + object iarray extends IArrayInstances + object int extends IntInstances + object invariant extends InvariantMonoidalInstances with InvariantInstances with InvariantInstancesBinCompat0 + object list extends ListInstances with ListInstancesBinCompat0 + object long extends LongInstances + object option extends OptionInstances with OptionInstancesBinCompat0 + object map extends MapInstances with MapInstancesBinCompat0 with MapInstancesBinCompat1 + object order extends OrderInstances + object ordering extends OrderingInstances + object parallel extends ParallelInstances + object partialOrder extends PartialOrderInstances + object partialOrdering extends PartialOrderingInstances + object queue extends QueueInstances + object set extends SetInstances + object seq extends SeqInstances + object short extends ShortInstances + object show extends ShowInstances + object sortedMap + extends SortedMapInstances + with SortedMapInstancesBinCompat0 + with SortedMapInstancesBinCompat1 + with SortedMapInstancesBinCompat2 + object sortedSet extends SortedSetInstances with SortedSetInstancesBinCompat0 with SortedSetInstancesBinCompat1 + + @deprecated("Use cats.instances.lazyList", "2.0.0-RC2") + object stream extends StreamInstances with StreamInstancesBinCompat0 + object lazyList extends LazyListInstances + object string extends StringInstances + object tailRec extends TailRecInstances + object try_ extends TryInstances + object tuple extends TupleInstances with Tuple2InstancesBinCompat0 + object unit extends UnitInstances + object uuid extends UUIDInstances + object vector extends VectorInstances with VectorInstancesBinCompat0 +} diff --git a/core/src/main/scala/cats/instances/StaticMethods.scala b/core/src/main/scala/cats/instances/StaticMethods.scala index 8df598bddd..cf6dc2d1bb 100644 --- a/core/src/main/scala/cats/instances/StaticMethods.scala +++ b/core/src/main/scala/cats/instances/StaticMethods.scala @@ -27,7 +27,13 @@ import scala.collection.mutable.Builder private[cats] object StaticMethods { - def appendAll[F <: Iterable[A], A](it: Iterator[F], bldr: Builder[A, F]): bldr.type = { + def appendAll[F <: IterableOnce[A], A](it: Iterator[F], bldr: Builder[A, F]): bldr.type = { + while (it.hasNext) { + bldr ++= it.next() + } + bldr + } + def appendAllMixed[F <: IterableOnce[A], A, Z[_]](it: Iterator[F], bldr: Builder[A, Z[A]]): bldr.type = { while (it.hasNext) { bldr ++= it.next() } diff --git a/kernel/src/main/scala-2.13+/cats/kernel/instances/AllInstances.scala b/kernel/src/main/scala-2.13/cats/kernel/instances/AllInstances.scala similarity index 100% rename from kernel/src/main/scala-2.13+/cats/kernel/instances/AllInstances.scala rename to kernel/src/main/scala-2.13/cats/kernel/instances/AllInstances.scala diff --git a/kernel/src/main/scala-3/cats/kernel/instances/AllInstances.scala b/kernel/src/main/scala-3/cats/kernel/instances/AllInstances.scala new file mode 100644 index 0000000000..6b78cc2fdb --- /dev/null +++ b/kernel/src/main/scala-3/cats/kernel/instances/AllInstances.scala @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2015 Typelevel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package cats.kernel +package instances + +trait AllInstances + extends ArraySeqInstances + with BigDecimalInstances + with BigIntInstances + with BitSetInstances + with BooleanInstances + with ByteInstances + with CharInstances + with DoubleInstances + with EqInstances + with EitherInstances + with DurationInstances + with FloatInstances + with FunctionInstances + with HashInstances + with IntInstances + with LazyListInstances + with ListInstances + with LongInstances + with MapInstances + with OptionInstances + with OrderInstances + with PartialOrderInstances + with QueueInstances + with SetInstances + with SeqInstances + with ShortInstances + with StreamInstances + with StringInstances + with SymbolInstances + with TupleInstances + with UnitInstances + with UUIDInstances + with VectorInstances + with IArrayInstances + +private[instances] trait AllInstancesBinCompat0 extends FiniteDurationInstances + +private[instances] trait AllInstancesBinCompat1 extends SortedMapInstances with SortedSetInstances + +private[instances] trait AllInstancesBinCompat2 extends DeadlineInstances diff --git a/kernel/src/main/scala-3/cats/kernel/instances/IArrayInstances.scala b/kernel/src/main/scala-3/cats/kernel/instances/IArrayInstances.scala new file mode 100644 index 0000000000..17120b58e1 --- /dev/null +++ b/kernel/src/main/scala-3/cats/kernel/instances/IArrayInstances.scala @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2015 Typelevel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package cats.kernel +package instances + +import scala.reflect.ClassTag +import compat.scalaVersionSpecific.* + +import scala.annotation.nowarn + +@suppressUnusedImportWarningForScalaVersionSpecific +trait IArrayInstances extends IArrayInstances1 { + implicit def catsKernelStdOrderForIArray[A: Order: ClassTag]: Order[IArray[A]] = + new IArrayOrder[A] + implicit def catsKernelStdMonoidForIArray[A: ClassTag]: Monoid[IArray[A]] = + IArrayMonoid[A] +} + +private[instances] trait IArrayInstances1 extends IArrayInstances2 { + implicit def catsKernelStdPartialOrderForIArray[A: PartialOrder]: PartialOrder[IArray[A]] = + new IArrayPartialOrder[A] + + implicit def catsKernelStdHashForIArray[A: Hash]: Hash[IArray[A]] = + new IArrayHash[A] +} + +private[instances] trait IArrayInstances2 { + implicit def catsKernelStdEqForIArray[A: Eq]: Eq[IArray[A]] = + new IArrayEq[A] +} + +class IArrayOrder[A](implicit ev: Order[A]) extends Order[IArray[A]] { + def compare(xs: IArray[A], ys: IArray[A]): Int = + if (xs eq ys) 0 + else StaticMethods.iteratorCompare(xs.iterator, ys.iterator) +} + +class IArrayPartialOrder[A](implicit ev: PartialOrder[A]) extends PartialOrder[IArray[A]] { + def partialCompare(xs: IArray[A], ys: IArray[A]): Double = + if (xs eq ys) 0.0 + else StaticMethods.iteratorPartialCompare(xs.iterator, ys.iterator) +} + +class IArrayHash[A](implicit ev: Hash[A]) extends IArrayEq[A] with Hash[IArray[A]] { + def hash(xs: IArray[A]): Int = StaticMethods.orderedHash(xs) +} + +class IArrayEq[A](implicit ev: Eq[A]) extends Eq[IArray[A]] { + def eqv(xs: IArray[A], ys: IArray[A]): Boolean = + if (xs eq ys) true + else StaticMethods.iteratorEq(xs.iterator, ys.iterator) +} + +private[instances] class IArrayMonoid[A](implicit ev: ClassTag[A]) extends Monoid[IArray[A]] { + def empty: IArray[A] = IArray.empty + def combine(x: IArray[A], y: IArray[A]): IArray[A] = x ++ y + + override def combineN(x: IArray[A], n: Int): IArray[A] = + val builder = IArray.newBuilder[A] + var i = n + while (i > 0) { builder ++= x.iterator; i -= 1 } + builder.result() + + override def combineAll(xs: IterableOnce[IArray[A]]): IArray[A] = + val builder = IArray.newBuilder[A] + xs.foreach(builder ++= _.iterator) + builder.result() +} + +object IArrayMonoid { + def apply[A: ClassTag]: Monoid[IArray[A]] = new IArrayMonoid[A] +} diff --git a/kernel/src/main/scala-3/cats/kernel/instances/iarray/package.scala b/kernel/src/main/scala-3/cats/kernel/instances/iarray/package.scala new file mode 100644 index 0000000000..1f918592b5 --- /dev/null +++ b/kernel/src/main/scala-3/cats/kernel/instances/iarray/package.scala @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2015 Typelevel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package cats.kernel +package instances + +package object iarray extends IArrayInstances 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 similarity index 100% rename from laws/src/main/scala-2.13+/cats/laws/discipline/ScalaVersionSpecific.scala rename to laws/src/main/scala-2.13/cats/laws/discipline/ScalaVersionSpecific.scala diff --git a/laws/src/main/scala-3/cats/laws/discipline/ScalaVersionSpecific.scala b/laws/src/main/scala-3/cats/laws/discipline/ScalaVersionSpecific.scala new file mode 100644 index 0000000000..7b3e5088a6 --- /dev/null +++ b/laws/src/main/scala-3/cats/laws/discipline/ScalaVersionSpecific.scala @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2015 Typelevel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package cats.laws.discipline + +import cats.data.{NonEmptyLazyList, ZipLazyList, ZipStream} +import org.scalacheck.{Arbitrary, Cogen} +import org.scalacheck.util.Buildable + +import scala.reflect.ClassTag + +private[discipline] object ScalaVersionSpecific { + + trait ArbitraryInstances { + + implicit def catsLawsCogenForIArray[A: ClassTag](implicit A: Cogen[A]): Cogen[IArray[A]] = + Cogen[Array[A]].contramap[IArray[A]](_.toArray) + + implicit def catsLawsBuildableForIArray[A: ClassTag]: Buildable[A, IArray[A]] = + new Buildable[A, IArray[A]]: + def builder = IArray.newBuilder[A] + + @deprecated("Use catsLawsArbitraryForZipLazyList", "2.0.0-RC2") + implicit def catsLawsArbitraryForZipStream[A](implicit A: Arbitrary[A]): Arbitrary[ZipStream[A]] = + Arbitrary(implicitly[Arbitrary[Stream[A]]].arbitrary.map(v => new ZipStream(v))) + + implicit def catsLawsArbitraryForZipLazyList[A](implicit A: Arbitrary[A]): Arbitrary[ZipLazyList[A]] = + Arbitrary(implicitly[Arbitrary[LazyList[A]]].arbitrary.map(v => new ZipLazyList(v))) + + 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 index 06ef0d18eb..9f4477d81e 100644 --- a/laws/src/main/scala/cats/laws/discipline/arbitrary.scala +++ b/laws/src/main/scala/cats/laws/discipline/arbitrary.scala @@ -32,6 +32,7 @@ import cats.data.* import org.scalacheck.{Arbitrary, Cogen, Gen} import org.scalacheck.Arbitrary.{arbitrary => getArbitrary} + /** * Arbitrary instances for cats.data */ @@ -43,6 +44,8 @@ object arbitrary extends ArbitraryInstances0 with ScalaVersionSpecific.Arbitrary implicit val catsLawsCogenForThrowable: Cogen[Throwable] = Cogen[String].contramap(_.toString) + + // this instance is not available in ScalaCheck 1.13.2. // remove this once a newer version is available. implicit def catsLawsCogenForTry[A](implicit A: Cogen[A]): Cogen[Try[A]] = diff --git a/tests/shared/src/test/scala-3/cats/tests/IArraySuite.scala b/tests/shared/src/test/scala-3/cats/tests/IArraySuite.scala new file mode 100644 index 0000000000..e65a2422f0 --- /dev/null +++ b/tests/shared/src/test/scala-3/cats/tests/IArraySuite.scala @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2015 Typelevel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package cats.tests + +import cats.{Align, Alternative, CoflatMap, Eval, Monad, Semigroupal, Traverse, TraverseFilter} +// import cats.data.{NonEmptyIArray, ZipIArray} +import cats.laws.discipline.{ + AlignTests, + AlternativeTests, + CoflatMapTests, + MonadTests, + SemigroupalTests, + SerializableTests, + ShortCircuitingTests, + TraverseFilterTests, + TraverseTests +} +import cats.laws.discipline.arbitrary.* +import cats.syntax.show.* +import cats.syntax.eq.* +import cats.instances.iarray.* +import org.scalacheck.Prop.* + +class IArraySuite extends CatsSuite { + checkAll("IArray[Int]", SemigroupalTests[IArray].semigroupal[Int, Int, Int]) + checkAll("Semigroupal[IArray]", SerializableTests.serializable(Semigroupal[IArray])) + + checkAll("IArray[Int]", CoflatMapTests[IArray].coflatMap[Int, Int, Int]) + checkAll("CoflatMap[IArray]", SerializableTests.serializable(CoflatMap[IArray])) + + checkAll("IArray[Int]", AlternativeTests[IArray].alternative[Int, Int, Int]) + checkAll("Alternative[IArray]", SerializableTests.serializable(Alternative[IArray])) + + // TraverseFilter behaviour discriminates on the Runtime type of the Applicative + checkAll("IArray[Int] with Option", TraverseTests[IArray].traverse[Int, Int, Int, Set[Int], Option, Option]) + checkAll("IArray[Int] with Eval", TraverseTests[IArray].traverse[Int, Int, Int, Set[Int], Eval, Eval]) + checkAll("Traverse[IArray]", SerializableTests.serializable(Traverse[IArray])) + + checkAll("IArray[Int]", MonadTests[IArray].monad[Int, Int, Int]) + checkAll("Monad[IArray]", SerializableTests.serializable(Monad[IArray])) + + checkAll("IArray[Int]", TraverseFilterTests[IArray].traverseFilter[Int, Int, Int]) + checkAll("TraverseFilter[IArray]", SerializableTests.serializable(TraverseFilter[IArray])) + + checkAll("IArray[Int]", AlignTests[IArray].align[Int, Int, Int, Int]) + checkAll("Align[IArray]", SerializableTests.serializable(Align[IArray])) + + checkAll("IArray[Int]", ShortCircuitingTests[IArray].traverseFilter[Int]) + checkAll("IArray[Int]", ShortCircuitingTests[IArray].foldable[Int]) + + test("show") { + assert(IArray(1, 2, 3).show === "IArray(1, 2, 3)") + + assert(IArray.empty[Int].show === "IArray()") + + forAll { (vec: IArray[String]) => + assert(vec.show === vec.mkString("IArray(", ", ", ")")) + } + } + + test("traverse is stack-safe") { + val vec = IArray.range(0, 1000000) + val sumAll = Traverse[IArray] + .traverse(vec) { i => () => i } + .apply() + .sum + + assert(sumAll == vec.sum) + } +} + +/* +final class IArrayInstancesSuite extends munit.FunSuite { + + test("NonEmptyParallel instance in cats.instances.IArray") { + import cats.instances.iarray.* + import cats.syntax.parallel.* + + (IArray(1, 2, 3), IArray("A", "B", "C")).parTupled + } +} + */