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

cats.evidence.As.scala Maven / Gradle / Ivy

The newest version!
package cats
package evidence

import arrow.Category

/**
 * As substitutability: A better `<:<`
 *
 * This class exists to aid in the capture proof of subtyping
 * relationships, which can be applied in other context to widen other
 * type
 *
 * `A As B` holds whenever `A` could be used in any negative context
 * that expects a `B`.  (e.g. if you could pass an `A` into any
 * function that expects a `B` as input.)
 *
 * This code was ported directly from scalaz to cats using this version from scalaz:
 * https://github.com/scalaz/scalaz/blob/a89b6d63/core/src/main/scala/scalaz/As.scala
 *
 *  The original contribution to scalaz came from Jason Zaugg
 */
sealed abstract class As[-A, +B] extends Serializable {

  /**
   * Use this subtyping relationship to replace B with a value of type
   * A in a contravariant context.  This would commonly be the input
   * to a function, such as F in: `type F[-B] = B => String`. In this
   * case, we could use A As B to turn an F[B] Into F[A].
   */
  def substitute[F[-_]](p: F[B]): F[A]

  @inline final def andThen[C](that: (B As C)): (A As C) = As.compose(that, this)

  @inline final def compose[C](that: (C As A)): (C As B) = As.compose(this, that)

  @inline final def coerce(a: A): B = As.witness(this)(a)
}

sealed abstract class AsInstances {
  import As._

  /*
   * Subtyping forms a category
   */
  implicit val liskov: Category[As] = new Category[As] {
    def id[A]: (A As A) = refl[A]

    def compose[A, B, C](bc: B As C, ab: A As B): (A As C) = bc.compose(ab)
  }
}

object As extends AsInstances {

  /**
   * In truth, "all values of `A Is B` are `refl`". `reflAny` is that
   * single value.
   */
  private[this] val reflAny = new (Any As Any) {
    def substitute[F[-_]](fa: F[Any]) = fa
  }

  /**
   * Subtyping is reflexive
   */
  implicit def refl[A]: (A As A) =
    reflAny.asInstanceOf[A As A]

  /**
   * We can witness the relationship by using it to make a substitution *
   */
  implicit def witness[A, B](lt: A As B): A => B = {
    type L[-α] = α => B
    lt.substitute[L](identity)
  }

  /**
   * Subtyping is transitive
   */
  def compose[A, B, C](f: B As C, g: A As B): A As C = {
    type L[-α] = α As C
    g.substitute[L](f)
  }

  /**
   * reify a subtype relationship as a Liskov relationship
   */
  @inline def reify[A, B >: A]: (A As B) = refl

  /**
   * It can be convenient to convert a <:< value into a `<~<` value.
   * This is not strictly valid as while it is almost certainly true that
   * `A <:< B` implies `A <~< B` it is not the case that you can create
   * evidence of `A <~< B` except via a coercion. Use responsibly.
   */
  def fromPredef[A, B](eq: A <:< B): A As B =
    reflAny.asInstanceOf[A As B]

  /**
   * We can lift subtyping into any covariant type constructor
   */
  def co[T[+_], A, A2](a: A As A2): (T[A] As T[A2]) = {
    type L[-α] = T[α] As T[A2]
    a.substitute[L](refl)
  }

  // Similarly, we can do this any time we find a covariant type
  // parameter. Here we provide the proof for what we expect to be the
  // most common shapes.

  def co2[T[+_, _], Z, A, B](a: A As Z): T[A, B] As T[Z, B] = {
    type L[-α] = T[α, B] As T[Z, B]
    a.substitute[L](refl)
  }

  /**
   * Widen a F[X,+A] to a F[X,B] if (A As B). This can be used to widen
   * the output of a Function1, for example.
   */
  def co2_2[T[_, +_], Z, A, B](a: B As Z): T[A, B] As T[A, Z] = {
    type L[-α] = T[A, α] As T[A, Z]
    a.substitute[L](refl)
  }

  def co3[T[+_, _, _], Z, A, B, C](a: A As Z): T[A, B, C] As T[Z, B, C] = {
    type L[-α] = T[α, B, C] As T[Z, B, C]
    a.substitute[L](refl)
  }

  def co3_2[T[_, +_, _], Z, A, B, C](a: B As Z): T[A, B, C] As T[A, Z, C] = {
    type L[-α] = T[A, α, C] As T[A, Z, C]
    a.substitute[L](refl)
  }

  def co3_3[T[+_, _, +_], Z, A, B, C](a: C As Z): T[A, B, C] As T[A, B, Z] = {
    type L[-α] = T[A, B, α] As T[A, B, Z]
    a.substitute[L](refl)
  }

  /**
   * Use this relationship to widen the output type of a Function1
   */
  def onF[X, A, B](ev: A As B)(fa: X => A): X => B = co2_2[Function1, B, X, A](ev).coerce(fa)

  /**
   * widen two types for binary type constructors covariant in both
   * parameters
   *
   * lift2(a,b) = co1_2(a) compose co2_2(b)
   */
  def lift2[T[+_, +_], A, A2, B, B2](
    a: A As A2,
    b: B As B2
  ): (T[A, B] As T[A2, B2]) = {
    type a[-X] = T[X, B2] As T[A2, B2]
    type b[-X] = T[A, X] As T[A2, B2]
    b.substitute[b](a.substitute[a](refl))
  }

  /**
   *  We can lift a subtyping relationship into a contravariant type
   *  constructor.
   *
   *  Given that F has the shape: F[-_], we show that:
   *     (A As B) implies (F[B] As F[A])
   */
  def contra[T[-_], A, B](a: A As B): (T[B] As T[A]) = {
    type L[-α] = T[B] As T[α]
    a.substitute[L](refl)
  }

  // Similarly, we can do this any time we find a contravariant type
  // parameter. Here we provide the proof for what we expect to be the
  // most common shapes.

  def contra1_2[T[-_, _], Z, A, B](a: A As Z): (T[Z, B] As T[A, B]) = {
    type L[-α] = T[Z, B] As T[α, B]
    a.substitute[L](refl)
  }

  def contra2_2[T[_, -_], Z, A, B](a: B As Z): (T[A, Z] As T[A, B]) = {
    type L[-α] = T[A, Z] As T[A, α]
    a.substitute[L](refl)
  }

  def contra1_3[T[-_, _, _], Z, A, B, C](a: A As Z): (T[Z, B, C] As T[A, B, C]) = {
    type L[-α] = T[Z, B, C] As T[α, B, C]
    a.substitute[L](refl)
  }

  def contra2_3[T[_, -_, _], Z, A, B, C](a: B As Z): (T[A, Z, C] As T[A, B, C]) = {
    type L[-α] = T[A, Z, C] As T[A, α, C]
    a.substitute[L](refl)
  }

  def contra3_3[T[_, _, -_], Z, A, B, C](a: C As Z): (T[A, B, Z] As T[A, B, C]) = {
    type L[-α] = T[A, B, Z] As T[A, B, α]
    a.substitute[L](refl)
  }

  /**
   * Use this relationship to narrow the input type of a Function1
   */
  def conF[A, B, C](ev: B As A)(fa: A => C): B => C =
    contra1_2[Function1, A, B, C](ev).coerce(fa)

  /**
   * Use this relationship to widen the output type and narrow the input type of a Function1
   */
  def invF[C, D, A, B](ev1: D As C, ev2: A As B)(fa: C => A): D => B =
    conF(ev1)(onF(ev2)(fa))
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy