Skip to content
Open
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 33 additions & 6 deletions core/src/main/scala/cats/data/OneAnd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ import kernel.compat.scalaVersionSpecific.*
* type NonEmptyStream[A] = OneAnd[Stream, A]
* }}}
*/
final case class OneAnd[F[_], A](head: A, tail: F[A]) {
final case class OneAnd[F[_], A](head: A, tail: F[A]) extends OneAndBinCompat0[F, A] {

/**
* Combine the head and tail into a single `F[A]` value.
*/
def unwrap(implicit F: Alternative[F]): F[A] =
def unwrap(implicit F: NonEmptyAlternative[F]): F[A] =
F.prependK(head, tail)

/**
Expand All @@ -53,7 +53,7 @@ final case class OneAnd[F[_], A](head: A, tail: F[A]) {
/**
* Append another OneAnd to this
*/
def combine(other: OneAnd[F, A])(implicit F: Alternative[F]): OneAnd[F, A] =
def combine(other: OneAnd[F, A])(implicit F: NonEmptyAlternative[F]): OneAnd[F, A] =
OneAnd(head, F.combineK(tail, other.unwrap))

/**
Expand Down Expand Up @@ -123,8 +123,21 @@ final case class OneAnd[F[_], A](head: A, tail: F[A]) {
s"OneAnd(${A.show(head)}, ${FA.show(tail)})"
}

private[data] trait OneAndBinCompat0[F[_], A] {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[question] Is this the right way to pass the MiMa check for a case class modification?

I saw #3997 (comment) and thought that this was the (only) way, but I could not find any other place that defines a similar bincompat trait (other than ValidatedFunctionsBinCompat0, which patches the companion rather than the Validated class itself).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we make this sealed? I think so.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I made it sealed : af23366

val head: A
val tail: F[A]
Copy link
Contributor

@satorg satorg May 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Abstract vals look suspicious a bit. And might not be necessary – since OneAndBinCompat0 is supposed to be extended by OneAnd only, a trick with self-type annotation might work here:

private[data] trait OneAndBinCompat0[F[_], A] {
  self: OneAnd[F, A] => // allow access to `head` and `tail` from withing this trait
    // no abstract `head` and `tail` should be necessary

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some reason I've never used a non-trait type for self-type annotation and couldn't come up with this!
Applied: af23366


@deprecated("Kept for binary compatibility", "2.14.0")
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[question] Is since = "2.14.0" specification appropriate (is that the next cats version)? Or do I just not need @deprecated at all since it's private[data]?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

personally I wouldn't add the deprecated and just add a comment.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a method is superceded by some other method with the same name, we usually not only make it package local, but also remove all implicit keywords from its definition, e.g.:

private[data] def combine(other: OneAnd[F, A])(F: Alternative[F]): OneAnd[F, A] = ...

which makes it less likely to interfere with the new method while preserving binary compatibility.

I personally don't mind marking it @deprecated but don't have strong opinion on that – it is not supposed to be called anyway.

private[data] def unwrap(implicit F: Alternative[F]): F[A] =
F.prependK(head, tail)

@deprecated("Kept for binary compatibility", "2.14.0")
private[data] def combine(other: OneAnd[F, A])(implicit F: Alternative[F]): OneAnd[F, A] =
OneAnd(head, F.combineK(tail, other.unwrap))
}

@suppressUnusedImportWarningForScalaVersionSpecific
sealed abstract private[data] class OneAndInstances extends OneAndLowPriority0 {
sealed abstract private[data] class OneAndInstances extends OneAndLowPriority0 with OneAndInstancesBinCompat0 {

implicit def catsDataParallelForOneAnd[A, M[_]: Alternative, F0[_]: Alternative](implicit
P: Parallel.Aux[M, F0]
Expand Down Expand Up @@ -158,13 +171,13 @@ sealed abstract private[data] class OneAndInstances extends OneAndLowPriority0 {

implicit def catsDataShowForOneAnd[A, F[_]](implicit A: Show[A], FA: Show[F[A]]): Show[OneAnd[F, A]] = _.show

implicit def catsDataSemigroupKForOneAnd[F[_]: Alternative]: SemigroupK[OneAnd[F, *]] =
implicit def catsDataSemigroupKForOneAnd[F[_]: NonEmptyAlternative]: SemigroupK[OneAnd[F, *]] =
new SemigroupK[OneAnd[F, *]] {
def combineK[A](a: OneAnd[F, A], b: OneAnd[F, A]): OneAnd[F, A] =
a.combine(b)
}

implicit def catsDataSemigroupForOneAnd[F[_]: Alternative, A]: Semigroup[OneAnd[F, A]] =
implicit def catsDataSemigroupForOneAnd[F[_]: NonEmptyAlternative, A]: Semigroup[OneAnd[F, A]] =
catsDataSemigroupKForOneAnd[F].algebra

implicit def catsDataMonadForOneAnd[F[_]](implicit
Expand Down Expand Up @@ -331,4 +344,18 @@ sealed abstract private[data] class OneAndLowPriority0 extends OneAndLowPriority
}
}

private[data] trait OneAndInstancesBinCompat0 {

@deprecated("Kept for binary compatibility", "2.14.0")
def catsDataSemigroupKForOneAnd[F[_]: Alternative]: SemigroupK[OneAnd[F, *]] =
new SemigroupK[OneAnd[F, *]] {
def combineK[A](a: OneAnd[F, A], b: OneAnd[F, A]): OneAnd[F, A] =
a.combine(b)
}

@deprecated("Kept for binary compatibility", "2.14.0")
def catsDataSemigroupForOneAnd[F[_]: Alternative, A]: Semigroup[OneAnd[F, A]] =
catsDataSemigroupKForOneAnd[F].algebra
}

object OneAnd extends OneAndInstances