-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4014 from satorg/alternative-prepend-append
[proposal] optimize Alternative (part 1): introduce NonEmptyAlternative with prependK and appendK methods
- Loading branch information
Showing
16 changed files
with
308 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package cats | ||
|
||
import simulacrum.typeclass | ||
import scala.annotation.implicitNotFound | ||
|
||
@implicitNotFound("Could not find an instance of NonEmptyAlternative for ${F}") | ||
@typeclass trait NonEmptyAlternative[F[_]] extends Applicative[F] with SemigroupK[F] { self => | ||
|
||
/** | ||
* Lift `a` into `F[_]` and prepend it to `fa`. | ||
* | ||
* Example: | ||
* {{{ | ||
* scala> NonEmptyAlternative[List].prependK(1, List(2, 3, 4)) | ||
* res0: List[Int] = List(1, 2, 3, 4) | ||
* }}} | ||
*/ | ||
def prependK[A](a: A, fa: F[A]): F[A] = combineK(pure(a), fa) | ||
|
||
/** | ||
* Lift `a` into `F[_]` and append it to `fa`. | ||
* | ||
* Example: | ||
* {{{ | ||
* scala> NonEmptyAlternative[List].appendK(List(1, 2, 3), 4) | ||
* res0: List[Int] = List(1, 2, 3, 4) | ||
* }}} | ||
*/ | ||
def appendK[A](fa: F[A], a: A): F[A] = combineK(fa, pure(a)) | ||
|
||
override def compose[G[_]: Applicative]: NonEmptyAlternative[λ[α => F[G[α]]]] = | ||
new ComposedNonEmptyAlternative[F, G] { | ||
val F = self | ||
val G = Applicative[G] | ||
} | ||
} | ||
|
||
object NonEmptyAlternative { | ||
/* ======================================================================== */ | ||
/* THE FOLLOWING CODE IS MANAGED BY SIMULACRUM; PLEASE DO NOT EDIT!!!! */ | ||
/* ======================================================================== */ | ||
|
||
/** | ||
* Summon an instance of [[NonEmptyAlternative]] for `F`. | ||
*/ | ||
@inline def apply[F[_]](implicit instance: NonEmptyAlternative[F]): NonEmptyAlternative[F] = instance | ||
|
||
@deprecated("Use cats.syntax object imports", "2.2.0") | ||
object ops { | ||
implicit def toAllNonEmptyAlternativeOps[F[_], A](target: F[A])(implicit tc: NonEmptyAlternative[F]): AllOps[F, A] { | ||
type TypeClassType = NonEmptyAlternative[F] | ||
} = new AllOps[F, A] { | ||
type TypeClassType = NonEmptyAlternative[F] | ||
val self: F[A] = target | ||
val typeClassInstance: TypeClassType = tc | ||
} | ||
} | ||
trait Ops[F[_], A] extends Serializable { | ||
type TypeClassType <: NonEmptyAlternative[F] | ||
def self: F[A] | ||
val typeClassInstance: TypeClassType | ||
// Note: `prependK` has to be added manually since simulacrum is not able to handle `self` as a second parameter. | ||
// def prependK(a: A): F[A] = typeClassInstance.prependK[A](a, self) | ||
def appendK(a: A): F[A] = typeClassInstance.appendK[A](self, a) | ||
} | ||
trait AllOps[F[_], A] extends Ops[F, A] with Applicative.AllOps[F, A] with SemigroupK.AllOps[F, A] { | ||
type TypeClassType <: NonEmptyAlternative[F] | ||
} | ||
trait ToNonEmptyAlternativeOps extends Serializable { | ||
implicit def toNonEmptyAlternativeOps[F[_], A](target: F[A])(implicit tc: NonEmptyAlternative[F]): Ops[F, A] { | ||
type TypeClassType = NonEmptyAlternative[F] | ||
} = new Ops[F, A] { | ||
type TypeClassType = NonEmptyAlternative[F] | ||
val self: F[A] = target | ||
val typeClassInstance: TypeClassType = tc | ||
} | ||
} | ||
@deprecated("Use cats.syntax object imports", "2.2.0") | ||
object nonInheritedOps extends ToNonEmptyAlternativeOps | ||
|
||
/* ======================================================================== */ | ||
/* END OF SIMULACRUM-MANAGED CODE */ | ||
/* ======================================================================== */ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package cats | ||
package syntax | ||
|
||
trait NonEmptyAlternativeSyntax { | ||
implicit final def catsSyntaxNonEmptyAlternative[F[_], A](fa: F[A]): NonEmptyAlternativeOps[F, A] = | ||
new NonEmptyAlternativeOps(fa) | ||
} | ||
|
||
final class NonEmptyAlternativeOps[F[_], A] private[syntax] (private val fa: F[A]) extends AnyVal { | ||
|
||
/** | ||
* @see [[NonEmptyAlternative.prependK]] | ||
* | ||
* Example: | ||
* {{{ | ||
* scala> import cats.syntax.all._ | ||
* | ||
* scala> List(2, 3, 4).prependK(1) | ||
* res0: List[Int] = List(1, 2, 3, 4) | ||
* }}} | ||
*/ | ||
def prependK(a: A)(implicit F: NonEmptyAlternative[F]): F[A] = F.prependK(a, fa) | ||
|
||
/** | ||
* @see [[NonEmptyAlternative.appendK]] | ||
* | ||
* Example: | ||
* {{{ | ||
* scala> import cats.syntax.all._ | ||
* | ||
* scala> List(1, 2, 3).appendK(4) | ||
* res0: List[Int] = List(1, 2, 3, 4) | ||
* }}} | ||
*/ | ||
def appendK(a: A)(implicit F: NonEmptyAlternative[F]): F[A] = F.appendK(fa, a) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
34 changes: 34 additions & 0 deletions
34
laws/src/main/scala/cats/laws/NonEmptyAlternativeLaws.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package cats | ||
package laws | ||
|
||
import cats.syntax.all._ | ||
|
||
trait NonEmptyAlternativeLaws[F[_]] extends ApplicativeLaws[F] with SemigroupKLaws[F] { | ||
implicit override def F: NonEmptyAlternative[F] | ||
implicit def algebra[A]: Semigroup[F[A]] = F.algebra[A] | ||
|
||
def nonEmptyAlternativeLeftDistributivity[A, B](fa: F[A], fa2: F[A], f: A => B): IsEq[F[B]] = | ||
((fa |+| fa2).map(f)) <-> ((fa.map(f)) |+| (fa2.map(f))) | ||
|
||
def nonEmptyAlternativeRightDistributivity[A, B](fa: F[A], ff: F[A => B], fg: F[A => B]): IsEq[F[B]] = | ||
((ff |+| fg).ap(fa)) <-> ((ff.ap(fa)) |+| (fg.ap(fa))) | ||
|
||
def nonEmptyAlternativePrependKConsitentWithPureAndCombineK[A](fa: F[A], a: A): IsEq[F[A]] = | ||
fa.prependK(a) <-> (a.pure[F] <+> fa) | ||
|
||
def nonEmptyAlternativeAppendKConsitentWithPureAndCombineK[A](fa: F[A], a: A): IsEq[F[A]] = | ||
fa.appendK(a) <-> (fa <+> a.pure[F]) | ||
} | ||
|
||
object NonEmptyAlternativeLaws { | ||
def apply[F[_]](implicit ev: NonEmptyAlternative[F]): NonEmptyAlternativeLaws[F] = | ||
new NonEmptyAlternativeLaws[F] { def F: NonEmptyAlternative[F] = ev } | ||
|
||
def composed[M[_], N[_]](implicit | ||
M: NonEmptyAlternative[M], | ||
N: Applicative[N] | ||
): NonEmptyAlternativeLaws[λ[α => M[N[α]]]] = | ||
new NonEmptyAlternativeLaws[λ[α => M[N[α]]]] { | ||
def F: NonEmptyAlternative[λ[α => M[N[α]]]] = M.compose[N] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.