Skip to content

Commit

Permalink
Selective has re-entered the chat
Browse files Browse the repository at this point in the history
  • Loading branch information
rossabaker committed Dec 21, 2020
1 parent 3bcb791 commit dba3dc0
Show file tree
Hide file tree
Showing 18 changed files with 231 additions and 190 deletions.
6 changes: 1 addition & 5 deletions core/src/main/scala/cats/Applicative.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package cats

import cats.arrow.Arrow
import simulacrum.{noop, typeclass}
import simulacrum.typeclass
import scala.annotation.implicitNotFound

/**
Expand Down Expand Up @@ -185,10 +185,6 @@ import scala.annotation.implicitNotFound
*/
def whenA[A](cond: Boolean)(f: => F[A]): F[Unit] =
if (cond) void(f) else unit

@noop
def whenS[A](fCond: F[Boolean])(fTrue: => F[Unit]): F[Unit] =
ifS(fCond)(fTrue)(unit)
}

object Applicative {
Expand Down
30 changes: 0 additions & 30 deletions core/src/main/scala/cats/Apply.scala
Original file line number Diff line number Diff line change
Expand Up @@ -257,30 +257,6 @@ trait Apply[F[_]] extends Functor[F] with InvariantSemigroupal[F] with ApplyArit
def ite(b: Boolean)(ifTrue: A, ifFalse: A) = if (b) ifTrue else ifFalse
ap2(map(fcond)(ite))(ifTrue, ifFalse)
}

def select[A, B](fab: F[Either[A, B]])(ff: => F[A => B]): F[B] =
selectA(fab)(ff)

def selectA[A, B](fab: F[Either[A, B]])(ff: F[A => B]): F[B] =
map2(fab, ff) {
case (Left(a), f) => f(a)
case (Right(b), _) => b
}

def branch[A, B, C](fab: F[Either[A, B]])(fl: => F[A => C])(fr: => F[B => C]): F[C] = {
val innerLhs: F[Either[A, Either[B, C]]] = map(fab)(_.map(Left(_)))
def innerRhs: F[A => Either[B, C]] = map(fl)(_.andThen(Right(_)))
val lhs = select(innerLhs)(innerRhs)
select(lhs)(fr)
}

@noop
def ifS[A](fCond: F[Boolean])(fTrue: => F[A])(fFalse: => F[A]): F[A] = {
val condition: F[Either[Unit, Unit]] = map(fCond)(if (_) EitherUtil.leftUnit else EitherUtil.unit)
def left: F[Unit => A] = map(fTrue)(Function.const)
def right: F[Unit => A] = map(fFalse)(Function.const)
branch(condition)(left)(right)
}
}

object Apply {
Expand Down Expand Up @@ -335,12 +311,6 @@ object Apply {
typeClassInstance.ap2[B, C, D](self.asInstanceOf[F[(B, C) => D]])(fa, fb)
def map2[B, C](fb: F[B])(f: (A, B) => C): F[C] = typeClassInstance.map2[A, B, C](self, fb)(f)
def map2Eval[B, C](fb: Eval[F[B]])(f: (A, B) => C): Eval[F[C]] = typeClassInstance.map2Eval[A, B, C](self, fb)(f)
def select[B, C](ff: => F[B => C])(implicit ev$1: A <:< Either[B, C]): F[C] =
typeClassInstance.select[B, C](self.asInstanceOf[F[Either[B, C]]])(ff)
def selectA[B, C](ff: F[B => C])(implicit ev$1: A <:< Either[B, C]): F[C] =
typeClassInstance.selectA[B, C](self.asInstanceOf[F[Either[B, C]]])(ff)
def branch[B, C, D](fl: => F[B => D])(fr: => F[C => D])(implicit ev$1: A <:< Either[B, C]): F[D] =
typeClassInstance.branch[B, C, D](self.asInstanceOf[F[Either[B, C]]])(fl)(fr)
}
trait AllOps[F[_], A] extends Ops[F, A] with Functor.AllOps[F, A] with InvariantSemigroupal.AllOps[F, A] {
type TypeClassType <: Apply[F]
Expand Down
4 changes: 2 additions & 2 deletions core/src/main/scala/cats/RigidSelective.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import simulacrum.typeclass
import scala.annotation.implicitNotFound

@implicitNotFound("Could not find an instance of RigidSelective for ${F}")
@typeclass trait RigidSelective[F[_]] extends Applicative[F]
@typeclass trait RigidSelective[F[_]] extends Selective[F]

object RigidSelective {
/* ======================================================================== */
Expand All @@ -31,7 +31,7 @@ object RigidSelective {
def self: F[A]
val typeClassInstance: TypeClassType
}
trait AllOps[F[_], A] extends Ops[F, A] with Applicative.AllOps[F, A] {
trait AllOps[F[_], A] extends Ops[F, A] with Selective.AllOps[F, A] {
type TypeClassType <: RigidSelective[F]
}
trait ToRigidSelectiveOps extends Serializable {
Expand Down
66 changes: 66 additions & 0 deletions core/src/main/scala/cats/Selective.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package cats

import simulacrum.typeclass
import scala.annotation.implicitNotFound

@implicitNotFound("Could not find an instance of Selective for ${F}")
@typeclass trait Selective[F[_]] extends Applicative[F] {
def select[A, B](fab: F[Either[A, B]])(ff: => F[A => B]): F[B]

def branch[A, B, C](fab: F[Either[A, B]])(fl: => F[A => C])(fr: => F[B => C]): F[C] = {
val innerLhs: F[Either[A, Either[B, C]]] = map(fab)(_.map(Left(_)))
def innerRhs: F[A => Either[B, C]] = map(fl)(_.andThen(Right(_)))
val lhs = select(innerLhs)(innerRhs)
select(lhs)(fr)
}
}

object Selective {
/* ======================================================================== */
/* THE FOLLOWING CODE IS MANAGED BY SIMULACRUM; PLEASE DO NOT EDIT!!!! */
/* ======================================================================== */

/**
* Summon an instance of [[Selective]] for `F`.
*/
@inline def apply[F[_]](implicit instance: Selective[F]): Selective[F] = instance

@deprecated("Use cats.syntax object imports", "2.2.0")
object ops {
implicit def toAllSelectiveOps[F[_], A](target: F[A])(implicit tc: Selective[F]): AllOps[F, A] {
type TypeClassType = Selective[F]
} = new AllOps[F, A] {
type TypeClassType = Selective[F]
val self: F[A] = target
val typeClassInstance: TypeClassType = tc
}
}
trait Ops[F[_], A] extends Serializable {
type TypeClassType <: Selective[F]
def self: F[A]
val typeClassInstance: TypeClassType
def select[B, C](ff: => F[B => C])(implicit ev$1: A <:< Either[B, C]): F[C] =
typeClassInstance.select[B, C](self.asInstanceOf[F[Either[B, C]]])(ff)
def branch[B, C, D](fl: => F[B => D])(fr: => F[C => D])(implicit ev$1: A <:< Either[B, C]): F[D] =
typeClassInstance.branch[B, C, D](self.asInstanceOf[F[Either[B, C]]])(fl)(fr)
}
trait AllOps[F[_], A] extends Ops[F, A] with Applicative.AllOps[F, A] {
type TypeClassType <: Selective[F]
}
trait ToSelectiveOps extends Serializable {
implicit def toSelectiveOps[F[_], A](target: F[A])(implicit tc: Selective[F]): Ops[F, A] {
type TypeClassType = Selective[F]
} = new Ops[F, A] {
type TypeClassType = Selective[F]
val self: F[A] = target
val typeClassInstance: TypeClassType = tc
}
}
@deprecated("Use cats.syntax object imports", "2.2.0")
object nonInheritedOps extends ToSelectiveOps

/* ======================================================================== */
/* END OF SIMULACRUM-MANAGED CODE */
/* ======================================================================== */

}
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/data/Validated.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1044,7 +1044,7 @@ sealed abstract private[data] class ValidatedInstances2 {
// scalastyle:off method.length
}

private[data] class ValidatedSelective[E: Semigroup] extends ValidatedApplicative[E] {
private[data] class ValidatedSelective[E: Semigroup] extends ValidatedApplicative[E] with Selective[Validated[E, *]] {
override def select[A, B](fab: Validated[E, Either[A, B]])(ff: => Validated[E, A => B]): Validated[E, B] =
fab match {
case Valid(Left(a)) => ff.map(_(a))
Expand Down
1 change: 1 addition & 0 deletions core/src/main/scala/cats/syntax/all.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ trait AllSyntax
with PartialOrderSyntax
with ProfunctorSyntax
with ReducibleSyntax
with SelectiveSyntax
with SemigroupSyntax
with SemigroupKSyntax
with ShowSyntax
Expand Down
6 changes: 0 additions & 6 deletions core/src/main/scala/cats/syntax/applicative.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ trait ApplicativeSyntax {
new ApplicativeIdOps[A](a)
implicit final def catsSyntaxApplicative[F[_], A](fa: F[A]): ApplicativeOps[F, A] =
new ApplicativeOps[F, A](fa)
implicit final def catsSyntaxApplicativeBoolean[F[_]](fBool: F[Boolean]): ApplicativeBooleanOps[F] =
new ApplicativeBooleanOps(fBool)
}

final class ApplicativeIdOps[A](private val a: A) extends AnyVal {
Expand All @@ -19,7 +17,3 @@ final class ApplicativeOps[F[_], A](private val fa: F[A]) extends AnyVal {
def unlessA(cond: Boolean)(implicit F: Applicative[F]): F[Unit] = F.unlessA(cond)(fa)
def whenA(cond: Boolean)(implicit F: Applicative[F]): F[Unit] = F.whenA(cond)(fa)
}

final class ApplicativeBooleanOps[F[_]](private val fCond: F[Boolean]) extends AnyVal {
def whenS[A](fTrue: => F[Unit])(implicit F: Applicative[F]): F[Unit] = F.whenS(fCond)(fTrue)
}
7 changes: 0 additions & 7 deletions core/src/main/scala/cats/syntax/apply.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ trait ApplySyntax extends TupleSemigroupalSyntax {

implicit final def catsSyntaxApplyOps[F[_], A](fa: F[A]): ApplyOps[F, A] =
new ApplyOps(fa)

implicit final def catsSyntaxApplyBooleanOps[F[_]](fBool: F[Boolean]): ApplyBooleanOps[F] =
new ApplyBooleanOps(fBool)
}

private[syntax] trait ApplySyntaxBinCompat0 {
Expand Down Expand Up @@ -71,7 +68,3 @@ final class ApplyOps[F[_], A](private val fa: F[A]) extends AnyVal {
@inline private[syntax] def forEffect[B](fb: F[B])(implicit F: Apply[F]): F[A] =
F.productL(fa)(fb)
}

final class ApplyBooleanOps[F[_]](private val fCond: F[Boolean]) extends AnyVal {
def ifS[A](fTrue: => F[A])(fFalse: => F[A])(implicit F: Apply[F]): F[A] = F.ifS(fCond)(fTrue)(fFalse)
}
1 change: 1 addition & 0 deletions core/src/main/scala/cats/syntax/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ package object syntax {
object profunctor extends ProfunctorSyntax
object reducible extends ReducibleSyntax with ReducibleSyntaxBinCompat0
object representable extends RepresentableSyntax
object selective extends SelectiveSyntax
object semigroup extends SemigroupSyntax
object semigroupal extends SemigroupalSyntax
object semigroupk extends SemigroupKSyntax
Expand Down
4 changes: 4 additions & 0 deletions core/src/main/scala/cats/syntax/selective.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package cats
package syntax

trait SelectiveSyntax extends Selective.ToSelectiveOps
11 changes: 0 additions & 11 deletions laws/src/main/scala/cats/laws/ApplicativeLaws.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package cats
package laws

import cats.syntax.apply._
import cats.syntax.applicative._
import cats.syntax.functor._

/**
Expand Down Expand Up @@ -47,16 +46,6 @@ trait ApplicativeLaws[F[_]] extends ApplyLaws[F] {

def monoidalRightIdentity[A](fa: F[A]): (F[(A, Unit)], F[A]) =
(F.product(fa, F.pure(())), fa)

def selectiveIdentity[A, B](faa: F[Either[A, A]]): IsEq[F[A]] =
faa.select[A, A](F.pure(identity)) <-> faa.map(_.merge)

def selectiveDistributivity[A, B](ab: Either[A, B], ff1: F[A => B], ff2: F[A => B]): IsEq[F[B]] =
F.pure(ab).select(ff1 *> ff2) <-> F.pure(ab).select(ff1) *> F.pure(ab).select(ff2)

def whenSIfSConsistency[A](fb: F[Boolean], fa: F[Unit]): IsEq[F[Unit]] = {
fb.whenS(fa) <-> fb.ifS(fa)(F.unit)
}
}

object ApplicativeLaws {
Expand Down
34 changes: 0 additions & 34 deletions laws/src/main/scala/cats/laws/ApplyLaws.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package cats
package laws

import cats.syntax.apply._
import cats.syntax.either._
import cats.syntax.functor._

/**
Expand All @@ -27,39 +26,6 @@ trait ApplyLaws[F[_]] extends FunctorLaws[F] with SemigroupalLaws[F] {

def productLConsistency[A, B](fa: F[A], fb: F[B]): IsEq[F[A]] =
F.productL(fa)(fb) <-> F.map2(fa, fb)((a, _) => a)

def selectAssociativity[A, B, C](fa: F[Either[A, B]], fb: F[Either[C, A => B]], fc: F[C => A => B]): IsEq[F[B]] = {
val fa0 = fa.map(_.map(_.asRight[(C, A)]))
val fb0 = fb.map { either => (a: A) => either.bimap(c => (c, a), f => f(a)) }
val fc0 = fc.map(Function.uncurried(_).tupled)
fa.select(fb.select(fc)) <-> fa0.select(fb0).select(fc0)
}

def branchConsistency[A, B, C](fab: F[Either[A, B]], fl: F[A => C], fr: F[B => C]): IsEq[F[C]] = {
fab.branch(fl)(fr) <-> {
val lhs = {
val innerLhs: F[Either[A, Either[B, C]]] = F.map(fab)(_.map(Left(_)))
val innerRhs: F[A => Either[B, C]] = F.map(fl)(_.andThen(Right(_)))
F.select(innerLhs)(innerRhs)
}
F.select(lhs)(fr)
}
}

def ifSConsistency[A](fb: F[Boolean], ft: F[A], ff: F[A]): IsEq[F[A]] = {
fb.ifS(ft)(ff) <-> {
val condition: F[Either[Unit, Unit]] = F.map(fb)(p => if (p) Left(()) else Right(()))
val left: F[Unit => A] = F.map(ft)(Function.const)
val right: F[Unit => A] = F.map(ff)(Function.const)
F.branch(condition)(left)(right)
}
}

def selectAConsistency[A, B](fab: F[Either[A, B]], ff: F[A => B]): IsEq[F[B]] =
F.selectA(fab)(ff) <-> F.map2(fab, ff) {
case (Left(a), f) => f(a)
case (Right(b), _) => b
}
}

object ApplyLaws {
Expand Down
13 changes: 2 additions & 11 deletions laws/src/main/scala/cats/laws/RigidSelectiveLaws.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import cats.syntax.all._
/**
* Laws that must be obeyed by any rigid `Selective`.
*/
trait RigidSelectiveLaws[F[_]] extends ApplicativeLaws[F] {
implicit override def F: Applicative[F]
trait RigidSelectiveLaws[F[_]] extends SelectiveLaws[F] {
implicit override def F: Selective[F]

def selectiveApply[A, B](fa: F[A], ff: F[A => B]): IsEq[F[B]] =
ff.ap(fa) <-> {
Expand All @@ -26,15 +26,6 @@ trait RigidSelectiveLaws[F[_]] extends ApplicativeLaws[F] {

def selectiveBranchSkipLeft[A, B, C](fb: F[B], fr: F[B => C]): IsEq[F[C]] =
fb.map(Right(_)).branch(ope[A => C])(fr) <-> fb.map(Left(_)).select(fr)

def selectiveIfSSkipFalse[A, B](fa: F[A], fb: F[B]): IsEq[F[B]] =
fa.as(true).ifS(fb)(ope[B]) <-> fa.as(Either.leftUnit).select(fb.map(Function.const))

def selectiveIfSSkipTrue[A, B](fa: F[A], fb: F[B]): IsEq[F[B]] =
fa.as(false).ifS(ope[B])(fb) <-> fa.as(Either.leftUnit).select(fb.map(Function.const))

def selectiveWhenSSkip[A](fa: F[A]): IsEq[F[Unit]] =
fa.as(false).whenS(ope[Unit]) <-> fa *> F.unit
}

object RigidSelectiveLaws {
Expand Down
41 changes: 41 additions & 0 deletions laws/src/main/scala/cats/laws/SelectiveLaws.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package cats
package laws

import cats.syntax.apply._
import cats.syntax.either._
import cats.syntax.functor._
import cats.syntax.selective._

/**
* Laws that must be obeyed by any `Selective`.
*/
trait SelectiveLaws[F[_]] extends ApplicativeLaws[F] {
implicit override def F: Selective[F]

def selectiveIdentity[A, B](faa: F[Either[A, A]]): IsEq[F[A]] =
faa.select[A, A](F.pure(identity)) <-> faa.map(_.merge)

def selectiveDistributivity[A, B](ab: Either[A, B], ff1: F[A => B], ff2: F[A => B]): IsEq[F[B]] =
F.pure(ab).select(ff1 *> ff2) <-> F.pure(ab).select(ff1) *> F.pure(ab).select(ff2)

def selectAssociativity[A, B, C](fa: F[Either[A, B]], fb: F[Either[C, A => B]], fc: F[C => A => B]): IsEq[F[B]] = {
val fa0 = fa.map(_.map(_.asRight[(C, A)]))
val fb0 = fb.map { either => (a: A) => either.bimap(c => (c, a), f => f(a)) }
val fc0 = fc.map(Function.uncurried(_).tupled)
fa.select(fb.select(fc)) <-> fa0.select(fb0).select(fc0)
}

def branchSelectConsistency[A, B, C](fab: F[Either[A, B]], fl: F[A => C], fr: F[B => C]): IsEq[F[C]] = {
fab.branch(fl)(fr) <-> {
val innerLhs: F[Either[A, Either[B, C]]] = F.map(fab)(_.map(Left(_)))
val innerRhs: F[A => Either[B, C]] = F.map(fl)(_.andThen(Right(_)))
val lhs = F.select(innerLhs)(innerRhs)
F.select(lhs)(fr)
}
}
}

object SelectiveLaws {
def apply[F[_]](implicit ev: Selective[F]): SelectiveLaws[F] =
new SelectiveLaws[F] { def F: Selective[F] = ev }
}
Loading

0 comments on commit dba3dc0

Please sign in to comment.