diff --git a/core/src/main/scala-2.12/cats/data/ChainCompanionCompat.scala b/core/src/main/scala-2.12/cats/data/ChainCompanionCompat.scala new file mode 100644 index 0000000000..d113e733f3 --- /dev/null +++ b/core/src/main/scala-2.12/cats/data/ChainCompanionCompat.scala @@ -0,0 +1,62 @@ +/* + * 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.data + +import cats.data.Chain.{nil, one, Wrap} +import cats.kernel.compat.scalaVersionSpecific.IterableOnce + +import scala.collection.immutable + +private[data] trait ChainCompanionCompat { + + /** + * Creates a Chain from the specified sequence. + */ + def fromSeq[A](s: Seq[A]): Chain[A] = + s match { + case imm: immutable.Seq[A] => fromImmutableSeq(imm) + case _ => fromMutableSeq(s) + } + + private def fromImmutableSeq[A](s: immutable.Seq[A]): Chain[A] = { + if (s.isEmpty) nil + else if (s.lengthCompare(1) == 0) one(s.head) + else Wrap(s) + } + + private def fromMutableSeq[A](s: Seq[A]): Chain[A] = { + if (s.isEmpty) nil + else if (s.lengthCompare(1) == 0) one(s.head) + else Wrap(s.toVector) + } + + /** + * Creates a Chain from the specified IterableOnce. + */ + def fromIterableOnce[A](xs: IterableOnce[A]): Chain[A] = + xs match { + case s: immutable.Seq[A] => fromImmutableSeq(s) // pay O(1) not O(N) cost + case s: Seq[A] => fromMutableSeq(s) + case notSeq => + fromImmutableSeq(notSeq.toVector) // toSeq could return a Stream, creating potential race conditions + } +} diff --git a/core/src/main/scala-2.13+/cats/data/ChainCompanionCompat.scala b/core/src/main/scala-2.13+/cats/data/ChainCompanionCompat.scala new file mode 100644 index 0000000000..1a8be19c79 --- /dev/null +++ b/core/src/main/scala-2.13+/cats/data/ChainCompanionCompat.scala @@ -0,0 +1,45 @@ +/* + * 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.data + +import cats.data.Chain.{nil, one, Wrap} + +private[data] trait ChainCompanionCompat { + + /** + * Creates a Chain from the specified sequence. + */ + def fromSeq[A](s: Seq[A]): Chain[A] = + if (s.isEmpty) nil + else if (s.lengthCompare(1) == 0) one(s.head) + else Wrap(s) + + /** + * Creates a Chain from the specified IterableOnce. + */ + def fromIterableOnce[A](xs: IterableOnce[A]): Chain[A] = Chain.fromSeq( + xs match { + case s: Seq[A] => s // pay O(1) not O(N) cost + case notSeq => notSeq.iterator.to(Vector) // toSeq could return a LazyList, creating potential race conditions + } + ) +} diff --git a/core/src/main/scala/cats/data/Chain.scala b/core/src/main/scala/cats/data/Chain.scala index 34117b6659..3a3f8b19ad 100644 --- a/core/src/main/scala/cats/data/Chain.scala +++ b/core/src/main/scala/cats/data/Chain.scala @@ -46,8 +46,8 @@ import cats.kernel.compat.scalaVersionSpecific._ import cats.kernel.instances.StaticMethods import scala.annotation.tailrec +import scala.collection.immutable import scala.collection.immutable.SortedMap -import scala.collection.immutable.{IndexedSeq => ImIndexedSeq} import scala.collection.mutable import scala.collection.mutable.ListBuffer @@ -904,7 +904,7 @@ sealed abstract class Chain[+A] extends ChainCompat[A] { } @suppressUnusedImportWarningForScalaVersionSpecific -object Chain extends ChainInstances { +object Chain extends ChainInstances with ChainCompanionCompat { private val sentinel: Function1[Any, Any] = new scala.runtime.AbstractFunction1[Any, Any] { def apply(a: Any) = this } @@ -937,7 +937,7 @@ object Chain extends ChainInstances { * The only places we create Wrap is in fromSeq and in methods that preserve * length: zipWithIndex, map, sort */ - final private[data] case class Wrap[A](seq: Seq[A]) extends NonEmpty[A] + final private[data] case class Wrap[A](seq: immutable.Seq[A]) extends NonEmpty[A] def unapplySeq[A](chain: Chain[A]): Option[Seq[A]] = Some(chain.toList) @@ -983,26 +983,6 @@ object Chain extends ChainInstances { def fromOption[A](o: Option[A]): Chain[A] = o.fold(Chain.empty[A])(Chain.one) - /** - * Creates a Chain from the specified sequence. - */ - def fromSeq[A](s: Seq[A]): Chain[A] = - if (s.isEmpty) nil - else if (s.lengthCompare(1) == 0) one(s.head) - else Wrap(s) - - /** - * Creates a Chain from the specified IterableOnce. - */ - def fromIterableOnce[A](xs: IterableOnce[A]): Chain[A] = - xs match { - case s: Seq[A @unchecked] => - // Seq is a subclass of IterableOnce, so the type has to be compatible - Chain.fromSeq(s) // pay O(1) not O(N) cost - case notSeq => - Chain.fromSeq(notSeq.iterator.toSeq) - } - /** * Creates a Chain from the specified elements. */ @@ -1010,7 +990,7 @@ object Chain extends ChainInstances { fromSeq(as) def traverseViaChain[G[_], A, B]( - as: ImIndexedSeq[A] + as: immutable.IndexedSeq[A] )(f: A => G[B])(implicit G: Applicative[G]): G[Chain[B]] = if (as.isEmpty) G.pure(Chain.nil) else { @@ -1056,7 +1036,7 @@ object Chain extends ChainInstances { } def traverseFilterViaChain[G[_], A, B]( - as: ImIndexedSeq[A] + as: immutable.IndexedSeq[A] )(f: A => G[Option[B]])(implicit G: Applicative[G]): G[Chain[B]] = if (as.isEmpty) G.pure(Chain.nil) else {