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

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

The newest version!
package cats.evidence

import cats.Id
import cats.arrow._

/**
 * A value of `A Is B` is proof that the types `A` and `B` are the same. More
 * powerfully, it asserts that they have the same meaning in all type
 * contexts. This can be a more powerful assertion than `A =:= B` and is more
 * easily used in manipulation of types while avoiding (potentially
 * erroneous) coercions.
 *
 * `A Is B` is also known as Leibniz equality.
 */
abstract class Is[A, B] extends Serializable {

  /**
   * To create an instance of `A Is B` you must show that for every choice
   * of `F[_]` you can convert `F[A]` to `F[B]`. Loosely, this reads as
   * saying that `B` must have the same effect as `A` in all contexts
   * therefore allowing type substitution.
   */
  def substitute[F[_]](fa: F[A]): F[B]

  /**
   * `Is` is transitive and therefore values of `Is` can be composed in a
   * chain much like functions. See also `compose`.
   */
  @inline final def andThen[C](next: B Is C): A Is C =
    next.substitute[Is[A, *]](this)

  /**
   * `Is` is transitive and therefore values of `Is` can be composed in a
   * chain much like functions. See also `andThen`.
   */
  @inline final def compose[C](prev: C Is A): C Is B =
    prev.andThen(this)

  /**
   * `Is` is symmetric and therefore can be flipped around. Flipping is its
   * own inverse, so `x.flip.flip == x`.
   */
  @inline final def flip: B Is A =
    this.substitute[Is[*, A]](Is.refl)

  /**
   * Sometimes for more complex substitutions it helps the typechecker to
   * wrap one layer of `F[_]` context around the types you're equating
   * before substitution.
   */
  @inline final def lift[F[_]]: F[A] Is F[B] =
    substitute[λ[α => F[A] Is F[α]]](Is.refl)

  /**
   * Substitution on identity brings about a direct coercion function of the
   * same form that `=:=` provides.
   */
  @inline final def coerce(a: A): B =
    substitute[Id](a)

  /**
   * A value `A Is B` is always sufficient to produce a similar `Predef.=:=`
   * value.
   */
  @inline final def predefEq: A =:= B =
    substitute[=:=[A, *]](implicitly[A =:= A])
}

sealed abstract class IsInstances {
  import Is._

  /**
   * The category instance on Leibniz categories.
   */
  implicit val leibniz: Category[Is] = new Category[Is] {
    def id[A]: A Is A = refl[A]
    def compose[A, B, C](bc: B Is C, ab: A Is B): A Is C = bc.compose(ab)
  }
}

object Is extends IsInstances {

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

  /**
   * In normal circumstances the only `Is` value which is available is the
   * computational identity `A Is A` at all types `A`. These "self loops"
   * generate all of the behavior of equality and also ensure that at its
   * heart `A Is B` is always just an identity relation.
   *
   * Implementation note: all values of `refl` return the same (private)
   * instance at whatever type is appropriate to save on allocations.
   */
  @inline implicit def refl[A]: A Is A =
    reflAny.asInstanceOf[A Is A]

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

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy