Set theoretic semantics of the type system

Not sure if I should post under Offtopic but anyway…

In Bezanson’s PhD thesis in 2015, he mentions that

Set-theoretic types are a natural basis for such a system. A set-theoretic type is a symbolic expression that denotes a set of values. In our case, these correspond to the sets of values methods are intended to apply to, or the sets of values supported by compiler-generated method specializations.

This is clearly reflected in the design of union types in Julia. We however notice that UnionAll types involve impredicative existential quantification (see e.g. Decidable Subtyping of Existential Types for Julia). Typically impredicative types quantifying over all types are not interpretable set theoretically.

So does Julia’s type system have a set theoretic interpretation?

The set theoretic interpretation is over values, i.e. one type is a subtype of another if the set of values for which isa is true on the LHS is a subset of the set of values for which isa is true on the RHS. Alternatively you can think of this as a projection to the concrete types. Note that there’s some messiness here around Type{}, although I’m hoping to clean that up in the near future.

Trying to wrap my head around all this type and set theory from nothing, what’s an example of this? If I’m reading this right, Vector{T} where T is one because of T<:Any, and that sounds like an impredicative set of Vector instances. Vector{Any} itself sounds like an impredicative set of Vector{Any} instances.

I might be stuck because I can’t imagine a neat parallel to the sets of sets that cause paradoxes. There are types of types: concrete DataType, universal Type, bounded Type{T} where A<:T<:B, singleton Type{A}, and their Unions. The simplest “set of all sets” paradox involves its power set, an impossible strict superset, but we can’t make arbitrary subtypes e.g. a type for a subset of another type’s instances like Base.instances(SignedTernaryInt) == (-1,0,1), and the Union of Type and all types is just Type.

Bit of an aside, is this really right?

To find a decidable yet practical fragment of Julia types, we conduct an empirical study, demonstrating that the types actually written by users are stratified. In particular, method type annotations can be stratified as method signatures that predicatively quantify over non-quantifying value types with use-site variance, which Julia already has a shorthand for (e.g. Vector{<:Number}). The key observation is that use-site variance is the only employed application of impredicative existential quantification where an existential type variable is instantiated with an existential type. One nice property of the stratification is that, aside from checking conservativity of bounds, it is syntactic. Our corpus analysis of all the source code of 9,335 Julia packages finds only a handful of stratification violations in 16.5 million lines of code.

From what I could understand, impredicative quantification can be as simple as

append!(a::Vector{T}, items::Union{Tuple, AbstractVector{<:T}}) where T
cache!(a::Vector{T}, item::T) where T
foo!(d::Dict{T,T}) where T

Is that really so rare? Or does it somehow mostly show up in Base?

I think you’re right: something is impredicative if its definition involves a quantification over itself. Because Vector <: Any, the definition Vector{T} where T is impredicative.

I’m stuck too (which is why I made the post) because I can’t think of any paradox due to impredicativity either. But still a set theoretic semantics (or domain theoretic semantics, or whatever) is lacking.

I think the thing that saves us from paradoxes is that the map from types to sets isn’t a bijection. every type corresponds to a set, but not every set corresponds to a type.

Yes, that’s also the overall idea I have. We can give a type two (and not one) interpretation: one is a set, and another is just an AST of its canonical form, and Type1 :: Type2 is to be understood as “the type tag Type1 is an element of the set denoted by Type2”. This neatly explains why we have Type :: Type, as the set denoted by Type is just a countable set of type expressions.

The question how to interpret something like A{T} where T however remains. Should the impredicative quantification be understood as going over all sets or all type expressions?

Not really. The type system does not have semantic subtyping. Furthermore, neither type intersection nor type negation exist.

An analogy does exist between sets and types: A <: B implies that the set of values of type A subsets the set of values of type B. The converse does not hold.

It is easy to conflate impredicativity in type theory with impredicativity in set theory. Many set-theoretic counterexamples, such as the diagonal argument or Russell’s paradox, exist because set theory permits certain constructions that are absent in type theory.

Consequently, Impredicativity does not inherently lead to contradictions; even in dependently typed languages, one must leverage additional constructions to formulate a counterexample.

For instance, if we ignore Type, DataType, and Any, Julia’s type system admits a straightforward set-theoretic interpretation. We can distinguish between two kinds of sets: sets of concrete values (whose elements are constructible Julia objects) and sets for abstract values (such as UnionAll, which represent unions of sets of concrete values). This framework does not encounter issues because the type universe is stratified. Russell’s paradox is impossible here because a Julia object is an element while a type is a set; they are distinct entities, making the self-membership x \in x impossible.

Regarding diagonal arguments: Julia lacks function types in the foundational sense, meaning it lacks the necessary powerset construction to violate cardinality (I think this is what you have in your mind, but you cannot directly reuse system F’s constructions here, as we don’t have higher-ranked functions).

Why, then, do Coq (and other dependently typed languages) or set theory encounter these paradoxes?

There are two primary reasons why these paradoxes emerge in such systems:

  1. Lack of Stratification: If a definition is not well-founded or stratified, paradoxes are more likely to arise. Set theory is essentially untyped, which facilitates the creation of self-referential loops such as x \in x. This is impossible in most statically typed languages, as they typically do not treat types as first-class values in a way that allows such circularity.
  2. Non-Constructibility: Paradoxes often stem from definitions that are not inherently constructible.

In Coq, for example, the system utilizes two distinct types of universes:

  • A single impredicative, non-constructible universe called Prop.
  • A hierarchy of predicative, constructible universes (\text{Set}_i), where each level \text{Set}_{i+1} is built from the preceding level \text{Set}_i.

The Prop universe admits a straightforward set-theoretic interpretation: a singleton set represents a true proposition, while an empty set represents a false one. This principle is known as proof irrelevance: if one has x : \text{Prop}, then x merely denotes the existence of a valid proof. Although multiple distinct proofs of a proposition may exist, the system does not distinguish between them, hence the singleton set representation.

In this context, function types do not trigger cardinality issues. Because functions are interpreted as logical implications (truth tables for A → B!), they also map to either a singleton or an empty set. Consequently, the system avoids the classical powerset interpretation that would otherwise lead to a violation of Cantor’s theorem.

Therefore, we cannot have strong elimination (pattern matching) within the Prop universe, which would otherwise allow for the destruction of a Prop value. Much like how one cannot analyze the internal structure of a simple boolean truth value, Props are non-constructible; their internal structures are opaque and cannot be analyzed. In summary, a formal contradiction arises only when the following three properties coexist:

Impredicativity + Proof Irrelevance + Strong Elimination = Contradiction

The point is that impredicativity is not inherently problematic; it only results in a contradiction if every possible interpretation is ruled out, but that requires the coexistence of many other powerful constructions.