Skip to content

Commit d14532f

Browse files
committed
add prependK/appendK for Cats NE collections
1 parent 5706752 commit d14532f

File tree

12 files changed

+170
-104
lines changed

12 files changed

+170
-104
lines changed

core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@ sealed abstract private[data] class NonEmptyLazyListInstances extends NonEmptyLa
488488

489489
implicit val catsDataInstancesForNonEmptyLazyList: Bimonad[NonEmptyLazyList]
490490
with NonEmptyTraverse[NonEmptyLazyList]
491-
with SemigroupK[NonEmptyLazyList]
491+
with NonEmptyAlternative[NonEmptyLazyList]
492492
with Align[NonEmptyLazyList] =
493493
new AbstractNonEmptyInstances[LazyList, NonEmptyLazyList] with Align[NonEmptyLazyList] {
494494

core/src/main/scala/cats/data/AbstractNonEmptyInstances.scala

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,23 @@ abstract private[data] class AbstractNonEmptyInstances[F[_], NonEmptyF[_]](impli
55
MF: Monad[F],
66
CF: CoflatMap[F],
77
TF: Traverse[F],
8-
SF: SemigroupK[F]
8+
SF: Alternative[F]
99
) extends Bimonad[NonEmptyF]
1010
with NonEmptyTraverse[NonEmptyF]
11-
with SemigroupK[NonEmptyF] {
11+
with NonEmptyAlternative[NonEmptyF] {
1212
val monadInstance = MF.asInstanceOf[Monad[NonEmptyF]]
1313
val coflatMapInstance = CF.asInstanceOf[CoflatMap[NonEmptyF]]
1414
val traverseInstance = Traverse[F].asInstanceOf[Traverse[NonEmptyF]]
15-
val semiGroupKInstance = SemigroupK[F].asInstanceOf[SemigroupK[NonEmptyF]]
15+
val alternativeInstance = Alternative[F].asInstanceOf[Alternative[NonEmptyF]]
1616

1717
def combineK[A](a: NonEmptyF[A], b: NonEmptyF[A]): NonEmptyF[A] =
18-
semiGroupKInstance.combineK(a, b)
18+
alternativeInstance.combineK(a, b)
19+
20+
override def prependK[A](a: A, fa: NonEmptyF[A]): NonEmptyF[A] =
21+
alternativeInstance.prependK(a, fa)
22+
23+
override def appendK[A](fa: NonEmptyF[A], a: A): NonEmptyF[A] =
24+
alternativeInstance.appendK(fa, a)
1925

2026
def pure[A](x: A): NonEmptyF[A] = monadInstance.pure(x)
2127

core/src/main/scala/cats/data/NonEmptyChain.scala

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -586,10 +586,17 @@ class NonEmptyChainOps[A](private val value: NonEmptyChain[A])
586586

587587
sealed abstract private[data] class NonEmptyChainInstances extends NonEmptyChainInstances1 {
588588

589-
implicit val catsDataInstancesForNonEmptyChain: SemigroupK[NonEmptyChain]
589+
// Required for binary compatibility with v2.6.1.
590+
def catsDataInstancesForNonEmptyChain: SemigroupK[NonEmptyChain]
590591
with NonEmptyTraverse[NonEmptyChain]
591592
with Bimonad[NonEmptyChain]
592593
with Align[NonEmptyChain] =
594+
catsDataInstancesForNonEmptyChainBinCompat1
595+
596+
implicit val catsDataInstancesForNonEmptyChainBinCompat1: Align[NonEmptyChain]
597+
with Bimonad[NonEmptyChain]
598+
with NonEmptyAlternative[NonEmptyChain]
599+
with NonEmptyTraverse[NonEmptyChain] =
593600
new AbstractNonEmptyInstances[Chain, NonEmptyChain] with Align[NonEmptyChain] {
594601
def extract[A](fa: NonEmptyChain[A]): A = fa.head
595602

core/src/main/scala/cats/data/NonEmptyList.scala

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -712,23 +712,36 @@ object NonEmptyList extends NonEmptyListInstances {
712712

713713
sealed abstract private[data] class NonEmptyListInstances extends NonEmptyListInstances0 {
714714

715+
// Required for binary compatibility with v2.6.1.
716+
def catsDataInstancesForNonEmptyList
717+
: SemigroupK[NonEmptyList] with Bimonad[NonEmptyList] with NonEmptyTraverse[NonEmptyList] with Align[NonEmptyList] =
718+
catsDataInstancesForNonEmptyListBinCompat1
719+
715720
/**
716721
* This is not a bug. The declared type of `catsDataInstancesForNonEmptyList` intentionally ignores
717722
* `NonEmptyReducible` trait for it not being a typeclass.
718723
*
719724
* Also see the discussion: PR #3541 and issue #3069.
720725
*/
721-
implicit val catsDataInstancesForNonEmptyList
722-
: SemigroupK[NonEmptyList] with Bimonad[NonEmptyList] with NonEmptyTraverse[NonEmptyList] with Align[NonEmptyList] =
726+
implicit val catsDataInstancesForNonEmptyListBinCompat1: NonEmptyAlternative[NonEmptyList]
727+
with Bimonad[NonEmptyList]
728+
with NonEmptyTraverse[NonEmptyList]
729+
with Align[NonEmptyList] =
723730
new NonEmptyReducible[NonEmptyList, List]
724-
with SemigroupK[NonEmptyList]
731+
with NonEmptyAlternative[NonEmptyList]
725732
with Bimonad[NonEmptyList]
726733
with NonEmptyTraverse[NonEmptyList]
727734
with Align[NonEmptyList] {
728735

729736
def combineK[A](a: NonEmptyList[A], b: NonEmptyList[A]): NonEmptyList[A] =
730737
a.concatNel(b)
731738

739+
override def prependK[A](a: A, fa: NonEmptyList[A]): NonEmptyList[A] =
740+
fa.prepend(a)
741+
742+
override def appendK[A](fa: NonEmptyList[A], a: A): NonEmptyList[A] =
743+
fa.append(a)
744+
732745
override def split[A](fa: NonEmptyList[A]): (A, List[A]) = (fa.head, fa.tail)
733746

734747
override def reduceLeft[A](fa: NonEmptyList[A])(f: (A, A) => A): A =

core/src/main/scala/cats/data/NonEmptySeq.scala

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -350,23 +350,36 @@ final class NonEmptySeq[+A] private (val toSeq: Seq[A]) extends AnyVal with NonE
350350
@suppressUnusedImportWarningForScalaVersionSpecific
351351
sealed abstract private[data] class NonEmptySeqInstances {
352352

353+
// Required for binary compatibility with v2.6.1.
354+
def catsDataInstancesForNonEmptySeq
355+
: SemigroupK[NonEmptySeq] with Bimonad[NonEmptySeq] with NonEmptyTraverse[NonEmptySeq] with Align[NonEmptySeq] =
356+
catsDataInstancesForNonEmptySeqBinCompat1
357+
353358
/**
354359
* This is not a bug. The declared type of `catsDataInstancesForNonEmptySeq` intentionally ignores
355360
* `NonEmptyReducible` trait for it not being a typeclass.
356361
*
357362
* Also see the discussion: PR #3541 and issue #3069.
358363
*/
359-
implicit val catsDataInstancesForNonEmptySeq
360-
: SemigroupK[NonEmptySeq] with Bimonad[NonEmptySeq] with NonEmptyTraverse[NonEmptySeq] with Align[NonEmptySeq] =
364+
implicit val catsDataInstancesForNonEmptySeqBinCompat1: NonEmptyAlternative[NonEmptySeq]
365+
with Bimonad[NonEmptySeq]
366+
with NonEmptyTraverse[NonEmptySeq]
367+
with Align[NonEmptySeq] =
361368
new NonEmptyReducible[NonEmptySeq, Seq]
362-
with SemigroupK[NonEmptySeq]
369+
with NonEmptyAlternative[NonEmptySeq]
363370
with Bimonad[NonEmptySeq]
364371
with NonEmptyTraverse[NonEmptySeq]
365372
with Align[NonEmptySeq] {
366373

367374
def combineK[A](a: NonEmptySeq[A], b: NonEmptySeq[A]): NonEmptySeq[A] =
368375
a.concatNeSeq(b)
369376

377+
override def prependK[A](a: A, fa: NonEmptySeq[A]): NonEmptySeq[A] =
378+
fa.prepend(a)
379+
380+
override def appendK[A](fa: NonEmptySeq[A], a: A): NonEmptySeq[A] =
381+
fa.append(a)
382+
370383
override def split[A](fa: NonEmptySeq[A]): (A, Seq[A]) = (fa.head, fa.tail)
371384

372385
override def size[A](fa: NonEmptySeq[A]): Long = fa.length.toLong

core/src/main/scala/cats/data/NonEmptyVector.scala

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -350,25 +350,38 @@ final class NonEmptyVector[+A] private (val toVector: Vector[A])
350350
@suppressUnusedImportWarningForScalaVersionSpecific
351351
sealed abstract private[data] class NonEmptyVectorInstances {
352352

353+
// Required for binary compatibility with v2.6.1.
354+
def catsDataInstancesForNonEmptyVector: SemigroupK[NonEmptyVector]
355+
with Bimonad[NonEmptyVector]
356+
with NonEmptyTraverse[NonEmptyVector]
357+
with Align[NonEmptyVector] =
358+
catsDataInstancesForNonEmptyVectorBinCompat1
359+
353360
/**
354361
* This is not a bug. The declared type of `catsDataInstancesForNonEmptyVector` intentionally ignores
355362
* `NonEmptyReducible` trait for it not being a typeclass.
356363
*
357364
* Also see the discussion: PR #3541 and issue #3069.
358365
*/
359-
implicit val catsDataInstancesForNonEmptyVector: SemigroupK[NonEmptyVector]
366+
implicit val catsDataInstancesForNonEmptyVectorBinCompat1: NonEmptyAlternative[NonEmptyVector]
360367
with Bimonad[NonEmptyVector]
361368
with NonEmptyTraverse[NonEmptyVector]
362369
with Align[NonEmptyVector] =
363370
new NonEmptyReducible[NonEmptyVector, Vector]
364-
with SemigroupK[NonEmptyVector]
371+
with NonEmptyAlternative[NonEmptyVector]
365372
with Bimonad[NonEmptyVector]
366373
with NonEmptyTraverse[NonEmptyVector]
367374
with Align[NonEmptyVector] {
368375

369376
def combineK[A](a: NonEmptyVector[A], b: NonEmptyVector[A]): NonEmptyVector[A] =
370377
a.concatNev(b)
371378

379+
override def prependK[A](a: A, fa: NonEmptyVector[A]): NonEmptyVector[A] =
380+
fa.prepend(a)
381+
382+
override def appendK[A](fa: NonEmptyVector[A], a: A): NonEmptyVector[A] =
383+
fa.append(a)
384+
372385
override def split[A](fa: NonEmptyVector[A]): (A, Vector[A]) = (fa.head, fa.tail)
373386

374387
override def size[A](fa: NonEmptyVector[A]): Long = fa.length.toLong
@@ -504,14 +517,14 @@ sealed abstract private[data] class NonEmptyVectorInstances {
504517
Show.show[NonEmptyVector[A]](_.show)
505518

506519
implicit def catsDataSemigroupForNonEmptyVector[A]: Semigroup[NonEmptyVector[A]] =
507-
catsDataInstancesForNonEmptyVector.algebra
520+
catsDataInstancesForNonEmptyVectorBinCompat1.algebra
508521

509522
implicit def catsDataParallelForNonEmptyVector: NonEmptyParallel.Aux[NonEmptyVector, ZipNonEmptyVector] =
510523
new NonEmptyParallel[NonEmptyVector] {
511524
type F[x] = ZipNonEmptyVector[x]
512525

513526
def apply: Apply[ZipNonEmptyVector] = ZipNonEmptyVector.catsDataCommutativeApplyForZipNonEmptyVector
514-
def flatMap: FlatMap[NonEmptyVector] = NonEmptyVector.catsDataInstancesForNonEmptyVector
527+
def flatMap: FlatMap[NonEmptyVector] = NonEmptyVector.catsDataInstancesForNonEmptyVectorBinCompat1
515528

516529
def sequential: ZipNonEmptyVector ~> NonEmptyVector =
517530
new (ZipNonEmptyVector ~> NonEmptyVector) { def apply[A](a: ZipNonEmptyVector[A]): NonEmptyVector[A] = a.value }

core/src/main/scala/cats/data/OneAnd.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ final case class OneAnd[F[_], A](head: A, tail: F[A]) {
1919
* Combine the head and tail into a single `F[A]` value.
2020
*/
2121
def unwrap(implicit F: Alternative[F]): F[A] =
22-
F.combineK(F.pure(head), tail)
22+
F.prependK(head, tail)
2323

2424
/**
2525
* remove elements not matching the predicate
@@ -33,7 +33,7 @@ final case class OneAnd[F[_], A](head: A, tail: F[A]) {
3333
* Append another OneAnd to this
3434
*/
3535
def combine(other: OneAnd[F, A])(implicit F: Alternative[F]): OneAnd[F, A] =
36-
OneAnd(head, F.combineK(tail, F.combineK(F.pure(other.head), other.tail)))
36+
OneAnd(head, F.combineK(tail, other.unwrap))
3737

3838
/**
3939
* find the first element matching the predicate, if one exists
@@ -222,7 +222,7 @@ sealed abstract private[data] class OneAndLowPriority2 extends OneAndLowPriority
222222
override def ap[A, B](ff: OneAnd[F, A => B])(fa: OneAnd[F, A]): OneAnd[F, B] = {
223223
val (f, tf) = (ff.head, ff.tail)
224224
val (a, ta) = (fa.head, fa.tail)
225-
val fb = F.ap(tf)(F.combineK(F.pure(a), ta))
225+
val fb = F.ap(tf)(F.prependK(a, ta))
226226
OneAnd(f(a), F.combineK(F.map(ta)(f), fb))
227227
}
228228
}

tests/src/test/scala-2.13+/cats/tests/NonEmptyLazyListSuite.scala

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,20 @@
11
package cats.tests
22

3-
import cats.{Align, Bimonad, SemigroupK, Show, Traverse}
4-
import cats.data.{NonEmptyLazyList, NonEmptyLazyListOps, NonEmptyVector}
5-
import cats.kernel.{Eq, Hash, Order, PartialOrder, Semigroup}
6-
import cats.kernel.laws.discipline.{EqTests, HashTests, OrderTests, PartialOrderTests, SemigroupTests}
7-
import cats.laws.discipline.{
8-
AlignTests,
9-
BimonadTests,
10-
NonEmptyTraverseTests,
11-
SemigroupKTests,
12-
SerializableTests,
13-
ShortCircuitingTests
14-
}
3+
import cats._
4+
import cats.data.NonEmptyLazyList
5+
import cats.data.NonEmptyLazyListOps
6+
import cats.data.NonEmptyVector
7+
import cats.kernel.laws.discipline.EqTests
8+
import cats.kernel.laws.discipline.HashTests
9+
import cats.kernel.laws.discipline.OrderTests
10+
import cats.kernel.laws.discipline.PartialOrderTests
11+
import cats.kernel.laws.discipline.SemigroupTests
12+
import cats.kernel.laws.discipline.SerializableTests
13+
import cats.laws.discipline._
1514
import cats.laws.discipline.arbitrary._
1615
import cats.syntax.either._
17-
import cats.syntax.foldable._
1816
import cats.syntax.eq._
19-
import cats.Reducible
20-
import cats.Eval
17+
import cats.syntax.foldable._
2118
import org.scalacheck.Prop._
2219

2320
class NonEmptyLazyListSuite extends NonEmptyCollectionSuite[LazyList, NonEmptyLazyList, NonEmptyLazyListOps] {
@@ -31,8 +28,10 @@ class NonEmptyLazyListSuite extends NonEmptyCollectionSuite[LazyList, NonEmptyLa
3128
checkAll(s"NonEmptyLazyList[Int]", HashTests[NonEmptyLazyList[Int]].hash)
3229
checkAll(s"Hash[NonEmptyLazyList[Int]]", SerializableTests.serializable(Hash[NonEmptyLazyList[Int]]))
3330

34-
checkAll("NonEmptyLazyList[Int]", SemigroupKTests[NonEmptyLazyList].semigroupK[Int])
35-
checkAll("SemigroupK[NonEmptyLazyList]", SerializableTests.serializable(SemigroupK[NonEmptyLazyList]))
31+
checkAll("NonEmptyLazyList[Int]", NonEmptyAlternativeTests[NonEmptyLazyList].nonEmptyAlternative[Int, Int, Int])
32+
checkAll("NonEmptyAlternative[NonEmptyLazyList]",
33+
SerializableTests.serializable(NonEmptyAlternative[NonEmptyLazyList])
34+
)
3635

3736
checkAll("NonEmptyLazyList[Int] with Option",
3837
NonEmptyTraverseTests[NonEmptyLazyList].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option]

tests/src/test/scala/cats/tests/NonEmptyChainSuite.scala

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,27 @@
11
package cats.tests
22

3-
import cats.{Align, Bimonad, SemigroupK, Show, Traverse}
4-
import cats.data.{Chain, NonEmptyChain, NonEmptyChainOps}
5-
import cats.kernel.{Eq, Order, PartialOrder, Semigroup}
6-
import cats.kernel.laws.discipline.{EqTests, OrderTests, PartialOrderTests, SemigroupTests}
7-
import cats.laws.discipline.{
8-
AlignTests,
9-
BimonadTests,
10-
NonEmptyTraverseTests,
11-
SemigroupKTests,
12-
SerializableTests,
13-
ShortCircuitingTests
14-
}
3+
import cats._
4+
import cats.data.Chain
5+
import cats.data.NonEmptyChain
6+
import cats.data.NonEmptyChainOps
7+
import cats.kernel.laws.discipline.EqTests
8+
import cats.kernel.laws.discipline.OrderTests
9+
import cats.kernel.laws.discipline.PartialOrderTests
10+
import cats.kernel.laws.discipline.SemigroupTests
11+
import cats.laws.discipline._
1512
import cats.laws.discipline.arbitrary._
1613
import cats.syntax.either._
17-
import cats.syntax.foldable._
1814
import cats.syntax.eq._
15+
import cats.syntax.foldable._
1916
import org.scalacheck.Prop._
2017

2118
class NonEmptyChainSuite extends NonEmptyCollectionSuite[Chain, NonEmptyChain, NonEmptyChainOps] {
2219
protected def toList[A](value: NonEmptyChain[A]): List[A] = value.toChain.toList
2320
protected def underlyingToList[A](underlying: Chain[A]): List[A] = underlying.toList
2421
protected def toNonEmptyCollection[A](nea: NonEmptyChain[A]): NonEmptyChainOps[A] = nea
2522

26-
checkAll("NonEmptyChain[Int]", SemigroupKTests[NonEmptyChain].semigroupK[Int])
27-
checkAll("SemigroupK[NonEmptyChain]", SerializableTests.serializable(SemigroupK[NonEmptyChain]))
23+
checkAll("NonEmptyChain[Int]", NonEmptyAlternativeTests[NonEmptyChain].nonEmptyAlternative[Int, Int, Int])
24+
checkAll("NonEmptyAlternative[NonEmptyChain]", SerializableTests.serializable(NonEmptyAlternative[NonEmptyChain]))
2825

2926
checkAll("NonEmptyChain[Int] with Option",
3027
NonEmptyTraverseTests[NonEmptyChain].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option]

tests/src/test/scala/cats/tests/NonEmptyListSuite.scala

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,27 @@
11
package cats.tests
22

3-
import cats.{Align, Bimonad, Eval, NonEmptyTraverse, Now, Reducible, SemigroupK, Show}
4-
import cats.data.{NonEmptyList, NonEmptyMap, NonEmptySet, NonEmptyVector}
3+
import cats._
4+
import cats.data.NonEmptyList
55
import cats.data.NonEmptyList.ZipNonEmptyList
6-
import cats.kernel.{Eq, Order, PartialOrder, Semigroup}
7-
import cats.kernel.laws.discipline.{EqTests, OrderTests, PartialOrderTests, SemigroupTests}
6+
import cats.data.NonEmptyMap
7+
import cats.data.NonEmptySet
8+
import cats.data.NonEmptyVector
9+
import cats.kernel.laws.discipline.EqTests
10+
import cats.kernel.laws.discipline.OrderTests
11+
import cats.kernel.laws.discipline.PartialOrderTests
12+
import cats.kernel.laws.discipline.SemigroupTests
13+
import cats.laws.discipline._
814
import cats.laws.discipline.arbitrary._
9-
import cats.laws.discipline.{
10-
AlignTests,
11-
BimonadTests,
12-
CommutativeApplyTests,
13-
NonEmptyTraverseTests,
14-
ReducibleTests,
15-
SemigroupKTests,
16-
SerializableTests,
17-
ShortCircuitingTests
18-
}
15+
import cats.syntax.eq._
1916
import cats.syntax.foldable._
2017
import cats.syntax.reducible._
2118
import cats.syntax.show._
22-
import scala.collection.immutable.{SortedMap, SortedSet}
23-
import cats.syntax.eq._
2419
import org.scalacheck.Prop._
2520
import org.scalacheck.Test.Parameters
2621

22+
import scala.collection.immutable.SortedMap
23+
import scala.collection.immutable.SortedSet
24+
2725
class NonEmptyListSuite extends NonEmptyCollectionSuite[List, NonEmptyList, NonEmptyList] {
2826
protected def toList[A](value: NonEmptyList[A]): List[A] = value.toList
2927
protected def underlyingToList[A](underlying: List[A]): List[A] = underlying
@@ -43,8 +41,8 @@ class NonEmptyListSuite extends NonEmptyCollectionSuite[List, NonEmptyList, NonE
4341
checkAll("NonEmptyList[Int]", ReducibleTests[NonEmptyList].reducible[Option, Int, Int])
4442
checkAll("Reducible[NonEmptyList]", SerializableTests.serializable(Reducible[NonEmptyList]))
4543

46-
checkAll("NonEmptyList[Int]", SemigroupKTests[NonEmptyList].semigroupK[Int])
47-
checkAll("SemigroupK[NonEmptyList[A]]", SerializableTests.serializable(SemigroupK[NonEmptyList]))
44+
checkAll("NonEmptyList[Int]", NonEmptyAlternativeTests[NonEmptyList].nonEmptyAlternative[Int, Int, Int])
45+
checkAll("NonEmptyAlternative[NonEmptyList[A]]", SerializableTests.serializable(NonEmptyAlternative[NonEmptyList]))
4846

4947
checkAll("NonEmptyList[Int]", SemigroupTests[NonEmptyList[Int]].semigroup)
5048
checkAll("Semigroup[NonEmptyList[Int]]", SerializableTests.serializable(Semigroup[NonEmptyList[Int]]))

0 commit comments

Comments
 (0)