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

cats.Applicative.scala Maven / Gradle / Ivy

The newest version!
package cats

import cats.arrow.Arrow
import simulacrum.typeclass

/**
 * Applicative functor.
 *
 * Allows application of a function in an Applicative context to a value in an Applicative context
 *
 * See: [[https://www.cs.ox.ac.uk/jeremy.gibbons/publications/iterator.pdf The Essence of the Iterator Pattern]]
 * Also: [[http://staff.city.ac.uk/~ross/papers/Applicative.pdf Applicative programming with effects]]
 *
 * Must obey the laws defined in cats.laws.ApplicativeLaws.
 */
@typeclass trait Applicative[F[_]] extends Apply[F] with InvariantMonoidal[F] { self =>

  /**
   * `pure` lifts any value into the Applicative Functor.
   *
   * Example:
   * {{{
   * scala> import cats.implicits._
   *
   * scala> Applicative[Option].pure(10)
   * res0: Option[Int] = Some(10)
   * }}}
   */
  def pure[A](x: A): F[A]

  /**
   * Returns an `F[Unit]` value, equivalent with `pure(())`.
   *
   * A useful shorthand, also allowing implementations to optimize the
   * returned reference (e.g. it can be a `val`).
   *
   * Example:
   * {{{
   * scala> import cats.implicits._
   *
   * scala> Applicative[Option].unit
   * res0: Option[Unit] = Some(())
   * }}}
   */
  def unit: F[Unit] = pure(())

  override def map[A, B](fa: F[A])(f: A => B): F[B] =
    ap(pure(f))(fa)

  /**
   * Given `fa` and `n`, apply `fa` `n` times to construct an `F[List[A]]` value.
   *
   * Example:
   * {{{
   * scala> import cats.data.State
   *
   * scala> type Counter[A] = State[Int, A]
   * scala> val getAndIncrement: Counter[Int] = State { i => (i + 1, i) }
   * scala> val getAndIncrement5: Counter[List[Int]] =
   *      | Applicative[Counter].replicateA(5, getAndIncrement)
   * scala> getAndIncrement5.run(0).value
   * res0: (Int, List[Int]) = (5,List(0, 1, 2, 3, 4))
   * }}}
   */
  def replicateA[A](n: Int, fa: F[A]): F[List[A]] =
    Traverse[List].sequence(List.fill(n)(fa))(this)

  /**
   * Compose an `Applicative[F]` and an `Applicative[G]` into an
   * `Applicative[λ[α => F[G[α]]]]`.
   *
   * Example:
   * {{{
   * scala> import cats.implicits._
   *
   * scala> val alo = Applicative[List].compose[Option]
   *
   * scala> alo.pure(3)
   * res0: List[Option[Int]] = List(Some(3))
   *
   * 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[_]: Applicative]: Applicative[λ[α => F[G[α]]]] =
    new ComposedApplicative[F, G] {
      val F = self
      val G = Applicative[G]
    }

  /**
   * Compose an `Applicative[F]` and a `ContravariantMonoidal[G]` into a
   * `ContravariantMonoidal[λ[α => F[G[α]]]]`.
   *
   * Example:
   * {{{
   * scala> import cats.kernel.Comparison
   * scala> import cats.implicits._
   *
   * // compares strings by alphabetical order
   * scala> val alpha: Order[String] = Order[String]
   *
   * // compares strings by their length
   * scala> val strLength: Order[String] = Order.by[String, Int](_.length)
   *
   * scala> val stringOrders: List[Order[String]] = List(alpha, strLength)
   *
   * // first comparison is with alpha order, second is with string length
   * scala> stringOrders.map(o => o.comparison("abc", "de"))
   * res0: List[Comparison] = List(LessThan, GreaterThan)
   *
   * scala> val le = Applicative[List].composeContravariantMonoidal[Order]
   *
   * // create Int orders that convert ints to strings and then use the string orders
   * scala> val intOrders: List[Order[Int]] = le.contramap(stringOrders)(_.toString)
   *
   * // first comparison is with alpha order, second is with string length
   * scala> intOrders.map(o => o.comparison(12, 3))
   * res1: List[Comparison] = List(LessThan, GreaterThan)
   *
   * // create the `product` of the string order list and the int order list
   * // `p` contains a list of the following orders:
   * // 1. (alpha comparison on strings followed by alpha comparison on ints)
   * // 2. (alpha comparison on strings followed by length comparison on ints)
   * // 3. (length comparison on strings followed by alpha comparison on ints)
   * // 4. (length comparison on strings followed by length comparison on ints)
   * scala> val p: List[Order[(String, Int)]] = le.product(stringOrders, intOrders)
   *
   * scala> p.map(o => o.comparison(("abc", 12), ("def", 3)))
   * res2: List[Comparison] = List(LessThan, LessThan, LessThan, GreaterThan)
   * }}}
   */
  def composeContravariantMonoidal[G[_]: ContravariantMonoidal]: ContravariantMonoidal[λ[α => F[G[α]]]] =
    new ComposedApplicativeContravariantMonoidal[F, G] {
      val F = self
      val G = ContravariantMonoidal[G]
    }

  /**
   * Returns the given argument (mapped to Unit) if `cond` is `false`,
   * otherwise, unit lifted into F.
   *
   * Example:
   * {{{
   * scala> import cats.implicits._
   *
   * scala> Applicative[List].unlessA(true)(List(1, 2, 3))
   * res0: List[Unit] = List(())
   *
   * scala> Applicative[List].unlessA(false)(List(1, 2, 3))
   * res1: List[Unit] = List((), (), ())
   *
   * scala> Applicative[List].unlessA(true)(List.empty[Int])
   * res2: List[Unit] = List(())
   *
   * scala> Applicative[List].unlessA(false)(List.empty[Int])
   * res3: List[Unit] = List()
   * }}}
   */
  def unlessA[A](cond: Boolean)(f: => F[A]): F[Unit] =
    if (cond) unit else void(f)

  /**
   * Returns the given argument (mapped to Unit) if `cond` is `true`, otherwise,
   * unit lifted into F.
   *
   * Example:
   * {{{
   * scala> import cats.implicits._
   *
   * scala> Applicative[List].whenA(true)(List(1, 2, 3))
   * res0: List[Unit] = List((), (), ())
   *
   * scala> Applicative[List].whenA(false)(List(1, 2, 3))
   * res1: List[Unit] = List(())
   *
   * scala> Applicative[List].whenA(true)(List.empty[Int])
   * res2: List[Unit] = List()
   *
   * scala> Applicative[List].whenA(false)(List.empty[Int])
   * res3: List[Unit] = List(())
   * }}}
   */
  def whenA[A](cond: Boolean)(f: => F[A]): F[Unit] =
    if (cond) void(f) else unit

}

object Applicative {
  def monoid[F[_], A](implicit f: Applicative[F], monoid: Monoid[A]): Monoid[F[A]] =
    new ApplicativeMonoid[F, A](f, monoid)

  /**
   * Creates an applicative functor for `F`, holding domain fixed and combining
   * over the codomain.
   *
   * Example:
   * {{{
   * scala> import cats.implicits._
   * scala> import cats.Applicative.catsApplicativeForArrow
   * scala> val toLong: Int => Long = _.toLong
   * scala> val double: Int => Int = 2*_
   * scala> val f: Int => (Long, Int) = catsApplicativeForArrow.product(toLong, double)
   * scala> f(3)
   * res0: (Long, Int) = (3,6)
   * }}}
   */
  private[cats] def catsApplicativeForArrow[F[_, _], A](implicit F: Arrow[F]): Applicative[F[A, *]] =
    new ArrowApplicative[F, A](F)

  /**
   * Creates a CoflatMap for an Applicative `F`.
   * Cannot be implicit in 1.0 for Binary Compatibility Reasons
   *
   * Example:
   * {{{
   * scala> import cats._
   * scala> import cats.implicits._
   * scala> val fa = Some(3)
   * fa: Option[Int] = Some(3)
   * scala> Applicative.coflatMap[Option].coflatten(fa)
   * res0: Option[Option[Int]] = Some(Some(3))
   * }}}
   */
  def coflatMap[F[_]](implicit F: Applicative[F]): CoflatMap[F] =
    new CoflatMap[F] {
      def coflatMap[A, B](fa: F[A])(f: F[A] => B): F[B] = F.pure(f(fa))
      def map[A, B](fa: F[A])(f: A => B): F[B] = F.map(fa)(f)
    }

}

private[cats] class ApplicativeMonoid[F[_], A](f: Applicative[F], monoid: Monoid[A])
    extends ApplySemigroup(f, monoid)
    with Monoid[F[A]] {
  def empty: F[A] = f.pure(monoid.empty)
}

private[cats] class ArrowApplicative[F[_, _], A](F: Arrow[F]) extends Applicative[F[A, *]] {
  def pure[B](b: B): F[A, B] = F.lift[A, B](_ => b)
  override def map[B, C](fb: F[A, B])(f: B => C): F[A, C] = F.rmap(fb)(f)
  def ap[B, C](ff: F[A, B => C])(fb: F[A, B]): F[A, C] =
    F.rmap(F.andThen(F.lift((x: A) => (x, x)), F.split(ff, fb)))(tup => tup._1(tup._2))
  override def product[B, C](fb: F[A, B], fc: F[A, C]): F[A, (B, C)] =
    F.andThen(F.lift((x: A) => (x, x)), F.split(fb, fc))
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy