Skip to content

Commit 475eff7

Browse files
committed
add prependK/appendK for Cats NE collections
1 parent 6de31de commit 475eff7

12 files changed

+172
-111
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ sealed abstract private[data] class NonEmptyLazyListInstances extends NonEmptyLa
509509

510510
implicit val catsDataInstancesForNonEmptyLazyList: Bimonad[NonEmptyLazyList]
511511
with NonEmptyTraverse[NonEmptyLazyList]
512-
with SemigroupK[NonEmptyLazyList]
512+
with NonEmptyAlternative[NonEmptyLazyList]
513513
with Align[NonEmptyLazyList] =
514514
new AbstractNonEmptyInstances[LazyList, NonEmptyLazyList] with Align[NonEmptyLazyList] {
515515

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

+10-4
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,23 @@ abstract private[data] class AbstractNonEmptyInstances[F[_], NonEmptyF[_]](impli
2626
MF: Monad[F],
2727
CF: CoflatMap[F],
2828
TF: Traverse[F],
29-
SF: SemigroupK[F]
29+
SF: Alternative[F]
3030
) extends Bimonad[NonEmptyF]
3131
with NonEmptyTraverse[NonEmptyF]
32-
with SemigroupK[NonEmptyF] {
32+
with NonEmptyAlternative[NonEmptyF] {
3333
val monadInstance = MF.asInstanceOf[Monad[NonEmptyF]]
3434
val coflatMapInstance = CF.asInstanceOf[CoflatMap[NonEmptyF]]
3535
val traverseInstance = Traverse[F].asInstanceOf[Traverse[NonEmptyF]]
36-
val semiGroupKInstance = SemigroupK[F].asInstanceOf[SemigroupK[NonEmptyF]]
36+
val alternativeInstance = Alternative[F].asInstanceOf[Alternative[NonEmptyF]]
3737

3838
def combineK[A](a: NonEmptyF[A], b: NonEmptyF[A]): NonEmptyF[A] =
39-
semiGroupKInstance.combineK(a, b)
39+
alternativeInstance.combineK(a, b)
40+
41+
override def prependK[A](a: A, fa: NonEmptyF[A]): NonEmptyF[A] =
42+
alternativeInstance.prependK(a, fa)
43+
44+
override def appendK[A](fa: NonEmptyF[A], a: A): NonEmptyF[A] =
45+
alternativeInstance.appendK(fa, a)
4046

4147
def pure[A](x: A): NonEmptyF[A] = monadInstance.pure(x)
4248

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

+11-1
Original file line numberDiff line numberDiff line change
@@ -617,10 +617,20 @@ class NonEmptyChainOps[A](private val value: NonEmptyChain[A])
617617

618618
sealed abstract private[data] class NonEmptyChainInstances extends NonEmptyChainInstances1 {
619619

620-
implicit val catsDataInstancesForNonEmptyChain: SemigroupK[NonEmptyChain]
620+
@deprecated(
621+
"maintained for the sake of binary compatibility only, use catsDataInstancesForNonEmptyChainBinCompat1 instead",
622+
"2.9.0"
623+
)
624+
def catsDataInstancesForNonEmptyChain: SemigroupK[NonEmptyChain]
621625
with NonEmptyTraverse[NonEmptyChain]
622626
with Bimonad[NonEmptyChain]
623627
with Align[NonEmptyChain] =
628+
catsDataInstancesForNonEmptyChainBinCompat1
629+
630+
implicit val catsDataInstancesForNonEmptyChainBinCompat1: Align[NonEmptyChain]
631+
with Bimonad[NonEmptyChain]
632+
with NonEmptyAlternative[NonEmptyChain]
633+
with NonEmptyTraverse[NonEmptyChain] =
624634
new AbstractNonEmptyInstances[Chain, NonEmptyChain] with Align[NonEmptyChain] {
625635
def extract[A](fa: NonEmptyChain[A]): A = fa.head
626636

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

+20-4
Original file line numberDiff line numberDiff line change
@@ -788,23 +788,39 @@ object NonEmptyList extends NonEmptyListInstances {
788788

789789
sealed abstract private[data] class NonEmptyListInstances extends NonEmptyListInstances0 {
790790

791+
@deprecated(
792+
"maintained for the sake of binary compatibility only - use catsDataInstancesForNonEmptyListBinCompat1 instead",
793+
"2.9.0"
794+
)
795+
def catsDataInstancesForNonEmptyList
796+
: SemigroupK[NonEmptyList] with Bimonad[NonEmptyList] with NonEmptyTraverse[NonEmptyList] with Align[NonEmptyList] =
797+
catsDataInstancesForNonEmptyListBinCompat1
798+
791799
/**
792800
* This is not a bug. The declared type of `catsDataInstancesForNonEmptyList` intentionally ignores
793801
* `NonEmptyReducible` trait for it not being a typeclass.
794802
*
795803
* Also see the discussion: PR #3541 and issue #3069.
796804
*/
797-
implicit val catsDataInstancesForNonEmptyList
798-
: SemigroupK[NonEmptyList] with Bimonad[NonEmptyList] with NonEmptyTraverse[NonEmptyList] with Align[NonEmptyList] =
805+
implicit val catsDataInstancesForNonEmptyListBinCompat1: NonEmptyAlternative[NonEmptyList]
806+
with Bimonad[NonEmptyList]
807+
with NonEmptyTraverse[NonEmptyList]
808+
with Align[NonEmptyList] =
799809
new NonEmptyReducible[NonEmptyList, List]
800-
with SemigroupK[NonEmptyList]
810+
with NonEmptyAlternative[NonEmptyList]
801811
with Bimonad[NonEmptyList]
802812
with NonEmptyTraverse[NonEmptyList]
803813
with Align[NonEmptyList] {
804814

805815
def combineK[A](a: NonEmptyList[A], b: NonEmptyList[A]): NonEmptyList[A] =
806816
a.concatNel(b)
807817

818+
override def prependK[A](a: A, fa: NonEmptyList[A]): NonEmptyList[A] =
819+
fa.prepend(a)
820+
821+
override def appendK[A](fa: NonEmptyList[A], a: A): NonEmptyList[A] =
822+
fa.append(a)
823+
808824
override def split[A](fa: NonEmptyList[A]): (A, List[A]) = (fa.head, fa.tail)
809825

810826
override def reduceLeft[A](fa: NonEmptyList[A])(f: (A, A) => A): A =
@@ -955,7 +971,7 @@ sealed abstract private[data] class NonEmptyListInstances extends NonEmptyListIn
955971
new NonEmptyParallel[NonEmptyList] {
956972
type F[x] = ZipNonEmptyList[x]
957973

958-
def flatMap: FlatMap[NonEmptyList] = NonEmptyList.catsDataInstancesForNonEmptyList
974+
def flatMap: FlatMap[NonEmptyList] = NonEmptyList.catsDataInstancesForNonEmptyListBinCompat1
959975

960976
def apply: Apply[ZipNonEmptyList] = ZipNonEmptyList.catsDataCommutativeApplyForZipNonEmptyList
961977

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

+21-5
Original file line numberDiff line numberDiff line change
@@ -381,23 +381,39 @@ final class NonEmptySeq[+A] private (val toSeq: Seq[A]) extends AnyVal with NonE
381381
@suppressUnusedImportWarningForScalaVersionSpecific
382382
sealed abstract private[data] class NonEmptySeqInstances {
383383

384+
@deprecated(
385+
"maintained for the sake of binary compatibility only - use catsDataInstancesForNonEmptySeqBinCompat1 instead",
386+
"2.9.0"
387+
)
388+
def catsDataInstancesForNonEmptySeq
389+
: SemigroupK[NonEmptySeq] with Bimonad[NonEmptySeq] with NonEmptyTraverse[NonEmptySeq] with Align[NonEmptySeq] =
390+
catsDataInstancesForNonEmptySeqBinCompat1
391+
384392
/**
385393
* This is not a bug. The declared type of `catsDataInstancesForNonEmptySeq` intentionally ignores
386394
* `NonEmptyReducible` trait for it not being a typeclass.
387395
*
388396
* Also see the discussion: PR #3541 and issue #3069.
389397
*/
390-
implicit val catsDataInstancesForNonEmptySeq
391-
: SemigroupK[NonEmptySeq] with Bimonad[NonEmptySeq] with NonEmptyTraverse[NonEmptySeq] with Align[NonEmptySeq] =
398+
implicit val catsDataInstancesForNonEmptySeqBinCompat1: NonEmptyAlternative[NonEmptySeq]
399+
with Bimonad[NonEmptySeq]
400+
with NonEmptyTraverse[NonEmptySeq]
401+
with Align[NonEmptySeq] =
392402
new NonEmptyReducible[NonEmptySeq, Seq]
393-
with SemigroupK[NonEmptySeq]
403+
with NonEmptyAlternative[NonEmptySeq]
394404
with Bimonad[NonEmptySeq]
395405
with NonEmptyTraverse[NonEmptySeq]
396406
with Align[NonEmptySeq] {
397407

398408
def combineK[A](a: NonEmptySeq[A], b: NonEmptySeq[A]): NonEmptySeq[A] =
399409
a.concatNeSeq(b)
400410

411+
override def prependK[A](a: A, fa: NonEmptySeq[A]): NonEmptySeq[A] =
412+
fa.prepend(a)
413+
414+
override def appendK[A](fa: NonEmptySeq[A], a: A): NonEmptySeq[A] =
415+
fa.append(a)
416+
401417
override def split[A](fa: NonEmptySeq[A]): (A, Seq[A]) = (fa.head, fa.tail)
402418

403419
override def size[A](fa: NonEmptySeq[A]): Long = fa.length.toLong
@@ -532,14 +548,14 @@ sealed abstract private[data] class NonEmptySeqInstances {
532548
implicit def catsDataShowForNonEmptySeq[A: Show]: Show[NonEmptySeq[A]] = _.show
533549

534550
implicit def catsDataSemigroupForNonEmptySeq[A]: Semigroup[NonEmptySeq[A]] =
535-
catsDataInstancesForNonEmptySeq.algebra
551+
catsDataInstancesForNonEmptySeqBinCompat1.algebra
536552

537553
implicit def catsDataParallelForNonEmptySeq: NonEmptyParallel.Aux[NonEmptySeq, ZipNonEmptySeq] =
538554
new NonEmptyParallel[NonEmptySeq] {
539555
type F[x] = ZipNonEmptySeq[x]
540556

541557
def apply: Apply[ZipNonEmptySeq] = ZipNonEmptySeq.catsDataCommutativeApplyForZipNonEmptySeq
542-
def flatMap: FlatMap[NonEmptySeq] = NonEmptySeq.catsDataInstancesForNonEmptySeq
558+
def flatMap: FlatMap[NonEmptySeq] = NonEmptySeq.catsDataInstancesForNonEmptySeqBinCompat1
543559

544560
def sequential: ZipNonEmptySeq ~> NonEmptySeq =
545561
new (ZipNonEmptySeq ~> NonEmptySeq) { def apply[A](a: ZipNonEmptySeq[A]): NonEmptySeq[A] = a.value }

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

+20-4
Original file line numberDiff line numberDiff line change
@@ -386,25 +386,41 @@ final class NonEmptyVector[+A] private (val toVector: Vector[A])
386386
@suppressUnusedImportWarningForScalaVersionSpecific
387387
sealed abstract private[data] class NonEmptyVectorInstances {
388388

389+
@deprecated(
390+
"maintained for the sake of binary compatibility only - use catsDataInstancesForNonEmptyChainBinCompat1 instead",
391+
"2.9.0"
392+
)
393+
def catsDataInstancesForNonEmptyVector: SemigroupK[NonEmptyVector]
394+
with Bimonad[NonEmptyVector]
395+
with NonEmptyTraverse[NonEmptyVector]
396+
with Align[NonEmptyVector] =
397+
catsDataInstancesForNonEmptyVectorBinCompat1
398+
389399
/**
390400
* This is not a bug. The declared type of `catsDataInstancesForNonEmptyVector` intentionally ignores
391401
* `NonEmptyReducible` trait for it not being a typeclass.
392402
*
393403
* Also see the discussion: PR #3541 and issue #3069.
394404
*/
395-
implicit val catsDataInstancesForNonEmptyVector: SemigroupK[NonEmptyVector]
405+
implicit val catsDataInstancesForNonEmptyVectorBinCompat1: NonEmptyAlternative[NonEmptyVector]
396406
with Bimonad[NonEmptyVector]
397407
with NonEmptyTraverse[NonEmptyVector]
398408
with Align[NonEmptyVector] =
399409
new NonEmptyReducible[NonEmptyVector, Vector]
400-
with SemigroupK[NonEmptyVector]
410+
with NonEmptyAlternative[NonEmptyVector]
401411
with Bimonad[NonEmptyVector]
402412
with NonEmptyTraverse[NonEmptyVector]
403413
with Align[NonEmptyVector] {
404414

405415
def combineK[A](a: NonEmptyVector[A], b: NonEmptyVector[A]): NonEmptyVector[A] =
406416
a.concatNev(b)
407417

418+
override def prependK[A](a: A, fa: NonEmptyVector[A]): NonEmptyVector[A] =
419+
fa.prepend(a)
420+
421+
override def appendK[A](fa: NonEmptyVector[A], a: A): NonEmptyVector[A] =
422+
fa.append(a)
423+
408424
override def split[A](fa: NonEmptyVector[A]): (A, Vector[A]) = (fa.head, fa.tail)
409425

410426
override def size[A](fa: NonEmptyVector[A]): Long = fa.length.toLong
@@ -550,14 +566,14 @@ sealed abstract private[data] class NonEmptyVectorInstances {
550566
implicit def catsDataShowForNonEmptyVector[A: Show]: Show[NonEmptyVector[A]] = _.show
551567

552568
implicit def catsDataSemigroupForNonEmptyVector[A]: Semigroup[NonEmptyVector[A]] =
553-
catsDataInstancesForNonEmptyVector.algebra
569+
catsDataInstancesForNonEmptyVectorBinCompat1.algebra
554570

555571
implicit def catsDataParallelForNonEmptyVector: NonEmptyParallel.Aux[NonEmptyVector, ZipNonEmptyVector] =
556572
new NonEmptyParallel[NonEmptyVector] {
557573
type F[x] = ZipNonEmptyVector[x]
558574

559575
def apply: Apply[ZipNonEmptyVector] = ZipNonEmptyVector.catsDataCommutativeApplyForZipNonEmptyVector
560-
def flatMap: FlatMap[NonEmptyVector] = NonEmptyVector.catsDataInstancesForNonEmptyVector
576+
def flatMap: FlatMap[NonEmptyVector] = NonEmptyVector.catsDataInstancesForNonEmptyVectorBinCompat1
561577

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

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ final case class OneAnd[F[_], A](head: A, tail: F[A]) {
4040
* Combine the head and tail into a single `F[A]` value.
4141
*/
4242
def unwrap(implicit F: Alternative[F]): F[A] =
43-
F.combineK(F.pure(head), tail)
43+
F.prependK(head, tail)
4444

4545
/**
4646
* remove elements not matching the predicate
@@ -54,7 +54,7 @@ final case class OneAnd[F[_], A](head: A, tail: F[A]) {
5454
* Append another OneAnd to this
5555
*/
5656
def combine(other: OneAnd[F, A])(implicit F: Alternative[F]): OneAnd[F, A] =
57-
OneAnd(head, F.combineK(tail, F.combineK(F.pure(other.head), other.tail)))
57+
OneAnd(head, F.combineK(tail, other.unwrap))
5858

5959
/**
6060
* find the first element matching the predicate, if one exists
@@ -239,7 +239,7 @@ sealed abstract private[data] class OneAndLowPriority2 extends OneAndLowPriority
239239
override def ap[A, B](ff: OneAnd[F, A => B])(fa: OneAnd[F, A]): OneAnd[F, B] = {
240240
val (f, tf) = (ff.head, ff.tail)
241241
val (a, ta) = (fa.head, fa.tail)
242-
val fb = F.ap(tf)(F.combineK(F.pure(a), ta))
242+
val fb = F.ap(tf)(F.prependK(a, ta))
243243
OneAnd(f(a), F.combineK(F.map(ta)(f), fb))
244244
}
245245
}

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

+16-17
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,21 @@
2121

2222
package cats.tests
2323

24-
import cats.{Align, Bimonad, SemigroupK, Show, Traverse}
25-
import cats.data.{NonEmptyLazyList, NonEmptyLazyListOps, NonEmptyVector}
26-
import cats.kernel.{Eq, Hash, Order, PartialOrder, Semigroup}
27-
import cats.kernel.laws.discipline.{EqTests, HashTests, OrderTests, PartialOrderTests, SemigroupTests}
28-
import cats.laws.discipline.{
29-
AlignTests,
30-
BimonadTests,
31-
NonEmptyTraverseTests,
32-
SemigroupKTests,
33-
SerializableTests,
34-
ShortCircuitingTests
35-
}
24+
import cats._
25+
import cats.data.NonEmptyLazyList
26+
import cats.data.NonEmptyLazyListOps
27+
import cats.data.NonEmptyVector
28+
import cats.kernel.laws.discipline.EqTests
29+
import cats.kernel.laws.discipline.HashTests
30+
import cats.kernel.laws.discipline.OrderTests
31+
import cats.kernel.laws.discipline.PartialOrderTests
32+
import cats.kernel.laws.discipline.SemigroupTests
33+
import cats.kernel.laws.discipline.SerializableTests
34+
import cats.laws.discipline._
3635
import cats.laws.discipline.arbitrary._
3736
import cats.syntax.either._
38-
import cats.syntax.foldable._
3937
import cats.syntax.eq._
40-
import cats.Reducible
41-
import cats.Eval
38+
import cats.syntax.foldable._
4239
import org.scalacheck.Prop._
4340

4441
class NonEmptyLazyListSuite extends NonEmptyCollectionSuite[LazyList, NonEmptyLazyList, NonEmptyLazyListOps] {
@@ -52,8 +49,10 @@ class NonEmptyLazyListSuite extends NonEmptyCollectionSuite[LazyList, NonEmptyLa
5249
checkAll(s"NonEmptyLazyList[Int]", HashTests[NonEmptyLazyList[Int]].hash)
5350
checkAll(s"Hash[NonEmptyLazyList[Int]]", SerializableTests.serializable(Hash[NonEmptyLazyList[Int]]))
5451

55-
checkAll("NonEmptyLazyList[Int]", SemigroupKTests[NonEmptyLazyList].semigroupK[Int])
56-
checkAll("SemigroupK[NonEmptyLazyList]", SerializableTests.serializable(SemigroupK[NonEmptyLazyList]))
52+
checkAll("NonEmptyLazyList[Int]", NonEmptyAlternativeTests[NonEmptyLazyList].nonEmptyAlternative[Int, Int, Int])
53+
checkAll("NonEmptyAlternative[NonEmptyLazyList]",
54+
SerializableTests.serializable(NonEmptyAlternative[NonEmptyLazyList])
55+
)
5756

5857
checkAll("NonEmptyLazyList[Int] with Option",
5958
NonEmptyTraverseTests[NonEmptyLazyList].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option]

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

+12-15
Original file line numberDiff line numberDiff line change
@@ -21,31 +21,28 @@
2121

2222
package cats.tests
2323

24-
import cats.{Align, Bimonad, SemigroupK, Show, Traverse}
25-
import cats.data.{Chain, NonEmptyChain, NonEmptyChainOps}
26-
import cats.kernel.{Eq, Order, PartialOrder, Semigroup}
27-
import cats.kernel.laws.discipline.{EqTests, OrderTests, PartialOrderTests, SemigroupTests}
28-
import cats.laws.discipline.{
29-
AlignTests,
30-
BimonadTests,
31-
NonEmptyTraverseTests,
32-
SemigroupKTests,
33-
SerializableTests,
34-
ShortCircuitingTests
35-
}
24+
import cats._
25+
import cats.data.Chain
26+
import cats.data.NonEmptyChain
27+
import cats.data.NonEmptyChainOps
28+
import cats.kernel.laws.discipline.EqTests
29+
import cats.kernel.laws.discipline.OrderTests
30+
import cats.kernel.laws.discipline.PartialOrderTests
31+
import cats.kernel.laws.discipline.SemigroupTests
32+
import cats.laws.discipline._
3633
import cats.laws.discipline.arbitrary._
3734
import cats.syntax.either._
38-
import cats.syntax.foldable._
3935
import cats.syntax.eq._
36+
import cats.syntax.foldable._
4037
import org.scalacheck.Prop._
4138

4239
class NonEmptyChainSuite extends NonEmptyCollectionSuite[Chain, NonEmptyChain, NonEmptyChainOps] {
4340
protected def toList[A](value: NonEmptyChain[A]): List[A] = value.toChain.toList
4441
protected def underlyingToList[A](underlying: Chain[A]): List[A] = underlying.toList
4542
protected def toNonEmptyCollection[A](nea: NonEmptyChain[A]): NonEmptyChainOps[A] = nea
4643

47-
checkAll("NonEmptyChain[Int]", SemigroupKTests[NonEmptyChain].semigroupK[Int])
48-
checkAll("SemigroupK[NonEmptyChain]", SerializableTests.serializable(SemigroupK[NonEmptyChain]))
44+
checkAll("NonEmptyChain[Int]", NonEmptyAlternativeTests[NonEmptyChain].nonEmptyAlternative[Int, Int, Int])
45+
checkAll("NonEmptyAlternative[NonEmptyChain]", SerializableTests.serializable(NonEmptyAlternative[NonEmptyChain]))
4946

5047
checkAll("NonEmptyChain[Int] with Option",
5148
NonEmptyTraverseTests[NonEmptyChain].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option]

0 commit comments

Comments
 (0)