All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cats.Apply.scala Maven / Gradle / Ivy

The newest version!
package cats

import simulacrum.{noop, typeclass}
import cats.data.Ior

/**
 * Weaker version of Applicative[F]; has apply but not pure.
 *
 * Must obey the laws defined in cats.laws.ApplyLaws.
 */
@typeclass(excludeParents = List("ApplyArityFunctions"))
trait Apply[F[_]] extends Functor[F] with InvariantSemigroupal[F] with ApplyArityFunctions[F] { self =>

  /**
   * Given a value and a function in the Apply context, applies the
   * function to the value.
   *
   * Example:
   * {{{
   * scala> import cats.implicits._
   *
   * scala> val someF: Option[Int => Long] = Some(_.toLong + 1L)
   * scala> val noneF: Option[Int => Long] = None
   * scala> val someInt: Option[Int] = Some(3)
   * scala> val noneInt: Option[Int] = None
   *
   * scala> Apply[Option].ap(someF)(someInt)
   * res0: Option[Long] = Some(4)
   *
   * scala> Apply[Option].ap(noneF)(someInt)
   * res1: Option[Long] = None
   *
   * scala> Apply[Option].ap(someF)(noneInt)
   * res2: Option[Long] = None
   *
   * scala> Apply[Option].ap(noneF)(noneInt)
   * res3: Option[Long] = None
   * }}}
   */
  def ap[A, B](ff: F[A => B])(fa: F[A]): F[B]

  /**
   * Compose two actions, discarding any value produced by the first.
   *
   * @see [[productL]] to discard the value of the second instead.
   *
   * Example:
   * {{{
   * scala> import cats.implicits._
   * scala> import cats.data.Validated
   * scala> import Validated.{Valid, Invalid}
   *
   * scala> type ErrOr[A] = Validated[String, A]
   *
   * scala> val validInt: ErrOr[Int] = Valid(3)
   * scala> val validBool: ErrOr[Boolean] = Valid(true)
   * scala> val invalidInt: ErrOr[Int] = Invalid("Invalid int.")
   * scala> val invalidBool: ErrOr[Boolean] = Invalid("Invalid boolean.")
   *
   * scala> Apply[ErrOr].productR(validInt)(validBool)
   * res0: ErrOr[Boolean] = Valid(true)
   *
   * scala> Apply[ErrOr].productR(invalidInt)(validBool)
   * res1: ErrOr[Boolean] = Invalid(Invalid int.)
   *
   * scala> Apply[ErrOr].productR(validInt)(invalidBool)
   * res2: ErrOr[Boolean] = Invalid(Invalid boolean.)
   *
   * scala> Apply[ErrOr].productR(invalidInt)(invalidBool)
   * res3: ErrOr[Boolean] = Invalid(Invalid int.Invalid boolean.)
   * }}}
   *
   */
  def productR[A, B](fa: F[A])(fb: F[B]): F[B] =
    ap(map(fa)(_ => (b: B) => b))(fb)

  /**
   * Compose two actions, discarding any value produced by the second.
   *
   * @see [[productR]] to discard the value of the first instead.
   *
   * Example:
   * {{{
   * scala> import cats.implicits._
   * scala> import cats.data.Validated
   * scala> import Validated.{Valid, Invalid}
   *
   * scala> type ErrOr[A] = Validated[String, A]
   *
   * scala> val validInt: ErrOr[Int] = Valid(3)
   * scala> val validBool: ErrOr[Boolean] = Valid(true)
   * scala> val invalidInt: ErrOr[Int] = Invalid("Invalid int.")
   * scala> val invalidBool: ErrOr[Boolean] = Invalid("Invalid boolean.")
   *
   * scala> Apply[ErrOr].productL(validInt)(validBool)
   * res0: ErrOr[Int] = Valid(3)
   *
   * scala> Apply[ErrOr].productL(invalidInt)(validBool)
   * res1: ErrOr[Int] = Invalid(Invalid int.)
   *
   * scala> Apply[ErrOr].productL(validInt)(invalidBool)
   * res2: ErrOr[Int] = Invalid(Invalid boolean.)
   *
   * scala> Apply[ErrOr].productL(invalidInt)(invalidBool)
   * res3: ErrOr[Int] = Invalid(Invalid int.Invalid boolean.)
   * }}}
   */
  def productL[A, B](fa: F[A])(fb: F[B]): F[A] =
    map2(fa, fb)((a, _) => a)

  override def product[A, B](fa: F[A], fb: F[B]): F[(A, B)] =
    ap(map(fa)(a => (b: B) => (a, b)))(fb)

  /** Alias for [[ap]]. */
  @inline final def <*>[A, B](ff: F[A => B])(fa: F[A]): F[B] =
    ap(ff)(fa)

  /** Alias for [[productR]]. */
  @inline final def *>[A, B](fa: F[A])(fb: F[B]): F[B] =
    productR(fa)(fb)

  /** Alias for [[productL]]. */
  @inline final def <*[A, B](fa: F[A])(fb: F[B]): F[A] =
    productL(fa)(fb)

  /** Alias for [[productR]]. */
  @deprecated("Use *> or productR instead.", "1.0.0-RC2")
  @noop @inline final private[cats] def followedBy[A, B](fa: F[A])(fb: F[B]): F[B] =
    productR(fa)(fb)

  /** Alias for [[productL]]. */
  @deprecated("Use <* or productL instead.", "1.0.0-RC2")
  @noop @inline final private[cats] def forEffect[A, B](fa: F[A])(fb: F[B]): F[A] =
    productL(fa)(fb)

  /**
   * ap2 is a binary version of ap, defined in terms of ap.
   */
  def ap2[A, B, Z](ff: F[(A, B) => Z])(fa: F[A], fb: F[B]): F[Z] =
    map(product(fa, product(fb, ff))) { case (a, (b, f)) => f(a, b) }

  /**
   * Applies the pure (binary) function f to the effectful values fa and fb.
   *
   * map2 can be seen as a binary version of [[cats.Functor]]#map.
   *
   * Example:
   * {{{
   * scala> import cats.implicits._
   *
   * scala> val someInt: Option[Int] = Some(3)
   * scala> val noneInt: Option[Int] = None
   * scala> val someLong: Option[Long] = Some(4L)
   * scala> val noneLong: Option[Long] = None
   *
   * scala> Apply[Option].map2(someInt, someLong)((i, l) => i.toString + l.toString)
   * res0: Option[String] = Some(34)
   *
   * scala> Apply[Option].map2(someInt, noneLong)((i, l) => i.toString + l.toString)
   * res0: Option[String] = None
   *
   * scala> Apply[Option].map2(noneInt, noneLong)((i, l) => i.toString + l.toString)
   * res0: Option[String] = None
   *
   * scala> Apply[Option].map2(noneInt, someLong)((i, l) => i.toString + l.toString)
   * res0: Option[String] = None
   * }}}
   */
  def map2[A, B, Z](fa: F[A], fb: F[B])(f: (A, B) => Z): F[Z] =
    map(product(fa, fb))(f.tupled)

  /**
   * Similar to [[map2]] but uses [[Eval]] to allow for laziness in the `F[B]`
   * argument. This can allow for "short-circuiting" of computations.
   *
   * NOTE: the default implementation of `map2Eval` does not short-circuit
   * computations. For data structures that can benefit from laziness, [[Apply]]
   * instances should override this method.
   *
   * In the following example, `x.map2(bomb)(_ + _)` would result in an error,
   * but `map2Eval` "short-circuits" the computation. `x` is `None` and thus the
   * result of `bomb` doesn't even need to be evaluated in order to determine
   * that the result of `map2Eval` should be `None`.
   *
   * {{{
   * scala> import cats.{Eval, Later}
   * scala> import cats.implicits._
   * scala> val bomb: Eval[Option[Int]] = Later(sys.error("boom"))
   * scala> val x: Option[Int] = None
   * scala> x.map2Eval(bomb)(_ + _).value
   * res0: Option[Int] = None
   * }}}
   */
  def map2Eval[A, B, Z](fa: F[A], fb: Eval[F[B]])(f: (A, B) => Z): Eval[F[Z]] =
    fb.map(fb => map2(fa, fb)(f))

  /**
   * Compose an `Apply[F]` and an `Apply[G]` into an `Apply[λ[α => F[G[α]]]]`.
   *
   * Example:
   * {{{
   * scala> import cats.implicits._
   *
   * scala> val alo = Apply[List].compose[Option]
   *
   * scala> alo.product(List(None, Some(true), Some(false)), List(Some(2), None))
   * res1: List[Option[(Boolean, Int)]] = List(None, None, Some((true,2)), None, Some((false,2)), None)
   * }}}
   */
  def compose[G[_]: Apply]: Apply[λ[α => F[G[α]]]] =
    new ComposedApply[F, G] {
      val F = self
      val G = Apply[G]
    }

  /**
   * An `if-then-else` lifted into the `F` context.
   * This function combines the effects of the `fcond` condition and of the two branches,
   * in the order in which they are given.
   *
   * The value of the result is, depending on the value of the condition,
   * the value of the first argument, or the value of the second argument.
   *
   * Example:
   * {{{
   * scala> import cats.implicits._
   *
   * scala> val b1: Option[Boolean] = Some(true)
   * scala> val asInt1: Option[Int] = Apply[Option].ifA(b1)(Some(1), Some(0))
   * scala> asInt1.get
   * res0: Int = 1
   *
   * scala> val b2: Option[Boolean] = Some(false)
   * scala> val asInt2: Option[Int] = Apply[Option].ifA(b2)(Some(1), Some(0))
   * scala> asInt2.get
   * res1: Int = 0
   *
   * scala> val b3: Option[Boolean] = Some(true)
   * scala> val asInt3: Option[Int] = Apply[Option].ifA(b3)(Some(1), None)
   * asInt2: Option[Int] = None
   *
   * }}}
   */
  @noop
  def ifA[A](fcond: F[Boolean])(ifTrue: F[A], ifFalse: F[A]): F[A] = {
    def ite(b: Boolean)(ifTrue: A, ifFalse: A) = if (b) ifTrue else ifFalse
    ap2(map(fcond)(ite))(ifTrue, ifFalse)
  }
}

object Apply {

  /**
   * This semigroup uses a product operation to combine `F`s.
   * If the `Apply[F].product` results in larger `F` (i.e. when `F` is a `List`),
   * accumulative usage of this instance, such as `combineAll`, will result in
   * `F`s with exponentially increasing sizes.
   */
  def semigroup[F[_], A](implicit f: Apply[F], sg: Semigroup[A]): Semigroup[F[A]] =
    new ApplySemigroup[F, A](f, sg)

  def align[F[_]: Apply]: Align[F] = new Align[F] {
    def align[A, B](fa: F[A], fb: F[B]): F[Ior[A, B]] = Apply[F].map2(fa, fb)(Ior.both)
    def functor: Functor[F] = Apply[F]
  }
}

private[cats] class ApplySemigroup[F[_], A](f: Apply[F], sg: Semigroup[A]) extends Semigroup[F[A]] {
  def combine(a: F[A], b: F[A]): F[A] =
    f.map2(a, b)(sg.combine)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy