Std.Equivalent

View source →

Propositional equality — the inductive identity type Equivalent(a, x, y) (spec 2026-07-04-identity-type-as-inductive).

Equivalent(a, x, y) is the proposition "x and y (both of type a) are the same thing". Its single constructor reflexive proves the only case that is always true — that a value equals itself — so a value of this type IS a proof of equality, checked by the dependent kernel (not a runtime token).

(Do not confuse this with Std.Equatable, the == comparison protocol. Equatable answers "are these equal?" with a runtime Bool; Equivalent is the type whose inhabitants are proofs that they are.)

The combinators work by matching on reflexive: once matched, the two endpoints are definitionally identified and the goal collapses to reflexivity. This is the technique Agda's Relation.Binary.PropositionalEquality uses.

Examples

cure
use Std.Equivalent

# given  p : Equivalent(a, x, y)
sym(p)              # : Equivalent(a, y, x)
trans(p, q)         # : Equivalent(a, x, z)   when  q : Equivalent(a, y, z)
cong(f, p)          # : Equivalent(b, f(x), f(y))

Group tag consumed by Cure.Stdlib.Preload. The identity type. The @builtin(:eq) decorator binds this declaration to the kernel's canonical identity family, so Equivalent and reflexive are ordinary, go-to-definition-able names even though the compiler gives them special dependent-typing treatment. reflexive(x) : Equivalent(a, x, x) is the sole constructor — the proof that x equals itself; its witness is erased at runtime.

Functions

  • # fn cong(a: Type, b: Type, x: a, y: a, f: Function(a, b), p: Equivalent(a, x, y)) -> Equivalent(b, f(x), f(y))

    Congruence: equal inputs give equal f-outputs.

  • # fn sym(a: Type, x: a, y: a, p: Equivalent(a, x, y)) -> Equivalent(a, y, x)

    Symmetry: from x = y derive y = x.

  • # fn trans(a: Type, x: a, y: a, z: a, p: Equivalent(a, x, y), q: Equivalent(a, y, z)) -> Equivalent(a, x, z)

    Transitivity: from x = y and y = z derive x = z.