-
Couldn't load subscription status.
- Fork 4
Home
Types in a language with subtyping form a partially ordered set.
Let's define:
-
a ~ b- type equality -
a < b- strict subtyping relation -
a ≤ b- subtyping relation -
a ≸ b- incomparable types
They satisfy a number of poset axioms & consequences of those axioms:
a ≤ b ⟺ a < b ⋁ a ~ ba < b ⟺ a ≤ b ⋀ ¬(a ~ b)¬(a ≤ b) ⟺ a ≸ b ⋁ b < a¬(a ~ b) ⟺ a ≸ b ⋁ b < a ⋁ a < b- ...
a ≸ b
⟺ ¬(a ~ b) ⋀ ¬(a < b) ⋀ ¬(b < a)
⟺ ¬(a ~ b ⋁ a < b ⋁ b < a)
⟺ ¬(a ≤ b ⋁ b ≤ a)
⟺ ¬(a ≤ b) ⋀ ¬(b ≤ a)
Define "type functions" as type constructors and type lambdas and write f a to mean type function f applied to type a.
Define:
-
constant fto mean∀ a b. f a ~ f b(constant function). -
injective fto mean∀ a b. f a ~ f b ⟶ a ~ b(injective function). -
covariant fto mean∀ a b. a ≤ b ⟶ f a ≤ f b(monotonically increasing function). -
contravariant fto mean∀ a b. a ≤ b ⟶ f b ≤ f a(monotonically decreasing function). -
strictly-covariant fto mean∀ a b. a < b ⟶ f a < f b(strictly increasing function). -
strictly-contravariant fto mean∀ a b. a < b ⟶ f b < f a(strictly decreasing function). -
invariant fto mean∀ a b. f b ≸ f a.
It is obvious that constant f ⟹ covariant f and strictly-covariant f ⟹ covariant f (analogously for contravariance). It is not obvious at all whether strictly-covariant f ⟹ covariant f ⋀ ¬(constant f).
At most one of strictly-covariant f, strictly-contravariant f, constant f, invariant f can be true simultaneously.
strictly-covariant f ⟹ ¬(contravariant f) and strictly-contravariant f ⟹ ¬(covariant f)
Proof:
WLOG we will only prove the first part. Suppose f is both strictly covariant and contravariant. Then for any a and b such that a < b, f a < f b and f b ≤ f a ⟺ f b < f a ⋁ f b ~ f a. Contradiction.
All type functions in Scala are either constant or injective. We can write it out as
⊤ ⟺ parametric f
⟺ (∀ a b. f a ~ f b) ⋁ (∀ a b. f a ~ f b ⟶ a ~ b)
⟺ ∀ a b x y. f x ~ f y ⋁ ¬(f a ~ f b) ⋁ a ~ b
All injective type functions are either strictly covariant, strictly contravariant, or invariant.
From the first parametricity axiom, we get:
- Lemma C:
∀ a b x y. f a ~ f b ⋀ ¬(a ~ b) ⟶ f x ~ f y. - Lemma I1:
∀ a b x y. ¬(f x ~ f y) ⋀ f a ~ f b ⟶ a ~ b. - Lemma I2:
∀ a b x y. ¬(f x ~ f y) ⋀ ¬(a ~ b) ⟶ ¬(f a ~ f b).
They can be declared in Scala as follows:
def lemmaC[F[_], A, B, X, Y](x: F[A] === F[B], y: (A === B) => Void): F[X] === F[Y]
def lemmaI1[F[_], A, B, X, Y](x: (F[X] === F[Y]) => Void, y: F[A] === F[B]): A === B
def lemmaI2[F[_], A, B, X, Y](x: (F[X] === F[Y]) => Void, y: (A === B) => Void): (F[A] === F[B]) => VoidClose examination shows that Lemma I2 can be constructively proven from Lemma I1.
We can show that covariant f ⟺ constant f ⋁ strictly-covariant f. Due to A1/A2, there are four cases to consider.
-
fis constant, both sides are true. -
fis strictly covariant, both sides are true. -
fis strictly contravariant, then by L2fis not covariant, both sides are false. -
fis invariant, both sides are false.
From the second parametricity axiom, we get:
⊤ ⟺ constant f ⋁ invariant f
⋁ strictly-covariant f ⋁ strictly-contravariant f
⟺ strictly-contravariant f ⋁ invariant f ⋁ (constant f ⋁ strictly-covariant f)
⟺ (∀ a b. ¬(a < b) ⋁ (f b < f a)) ⋁ (∀ a b. f a ≸ f b) ⋁ covariant f
⟹ ∀ a b. ¬(a < b) ⋁ (f b < f a) ⋁ (f c ≸ f d) ⋁ covariant f
⟺ ∀ a b. ¬(a < b) ⋁ ¬(f a ≤ f b) ⋁ covariant f
⟺ ∀ a b. ¬(a < b) ⋁ ¬(f a ≤ f b) ⋁ covariant f
⟺ ∀ a b. a < b ⋀ f a ≤ f b ⋀ covariant f
⟺ ∀ a b. a < b ⋀ f a ≤ f b ⟶ (∀ x y. x ≤ y ⟶ f x ≤ f y)
⟺ ∀ a b. ¬(a < b) ⋁ ¬(f a ≤ f b) ⋁ covariant f
⟺ ∀ a b x y. ¬(a < b) ⋁ ¬(f a ≤ f b) ⋁ ¬(x ≤ y) ⋁ f x ≤ f y
⟺ ∀ a b x y. a < b ⋀ f a ≤ f b ⋀ x ≤ y ⟶ f x ≤ f y
We will call this result Lemma S. It can be declared in Scala as:
def lemmaS[F[_], A, B, X, Y](ab: (A === B) => Void, p: A <~< B, q: F[A] <~< F[B], r: X <~< Y): F[X] <~< F[Y]trait Is[A, B] { def subst[F[_]](fa: F[A]): F[B] }
trait As[-A, +B] { def subst[F[+_]](fa: F[A]): F[B] }
type Void <: Nothing
type ¬[-A] = A => Void
type ⋁[+A, +B] = Either[A, B]
type ~[ A, B] = Is[A, B]
type ≤[-A, +B] = As[A, B]
type <[ A, B] = (¬[A ~ B], A ≤ B)
type ≸[ A, B] = (¬[A ≤ B], ¬[B ≤ A])
trait Constant[F[_]] {
def apply[A, B]: F[A] ~ F[B]
}
trait Injective[F[_]] {
def apply[A, B](ev: F[A] ~ F[B]): A ~ B
}
trait Invariant[F[_]] {
def apply[A, B]: F[A] ≸ F[B]
}
trait Covariant[F[_]] {
def apply[A, B](ev: A ≤ B): F[A] ≤ F[B]
}
trait Contravariant[F[_]] {
def apply[A, B](ev: A ≤ B): F[B] ≤ F[A]
}
trait StrictlyCovariant[F[_]] {
def apply[A, B](ev: A < B): F[A] < F[B]
}
trait StrictlyContravariant[F[_]] {
def apply[A, B](ev: A < B): F[B] < F[A]
}
def axiom1[F[_]]: ¬[¬[Constant[F] ⋁ Injective[F]]]
def axiom2[F[_]]: ¬[¬[Constant[F] ⋁ Invariant[F] ⋁ StrictlyCovariant[F] ⋁ StrictlyContravariant[F]]]
def axiom3[A, B](ev: ¬[¬[A ~ B]]): A ~ B
def axiom4[A, B](ev: ¬[¬[A ≤ B]]): A ≤ B