Static typing

Certain languages are "statically-typed." This means the compiler is aware of data-types at compile-time. Python and JavaScript are not statically-typed but C, Java, Haskell, and Rust are.

Curry-Howard correspondence

There's a thing called the Curry-Howard correspondence. (Also called the Curry-Howard isomorphism) It states that statically-typed computer programs are isomorphic with mathematical proofs. By "isomorphic," this means that each program has an equivalent proof, and each proof has an equivalent program. Every program can be thought of as a proof. and every proof can be thought of as a program.

The more advanced a type system is, the more advanced logic it can represent (propositional logic, first order logic, higher-order logic). Some languages are designed for logic proofs, like Coq, Idris, and Agda. These have expressive type systems, which makes them well-suited for proofs.

Propositions as types

In logic, propositions are statements that can be true or false, proven or unproven. Examples of propositions are \[1 = 0\], \[2 + 2 = 4\], and "the sky is orange." In the Curry-Howard Isomorphism, propositions are types and proofs are values. The concept of "a proposition having a proof" is "a type having a value." For example, true is a proof of the proposition bool , and 10 is a proof of the proposition int . This isn't very meaningful. Not all programs are useful when thought of as proofs, and not all proofs are useful when thought of as programs. The isomorphism is more useful for fancier types/propositions.

True as unit type

In logic, \[\top\] is a proposition that is defined to be true. It has a proof, by definition. In some programming languages, there's a unit type that has only one value. If C allowed empty structs, that would be a unit type, since there's only one value an empty struct can have. In Haskell and Rust, the unit type is an empty tuple () . Since a type having a value is isomorphic with a proposition having a proof, the unit type is isomorphic with \[\top\]. The unit type has one value, and \[\top\] has one proof.

False as bottom type

In logic, \[\bot\] is a proposition that has no proofs. It is considered "false." Some programming languages have a "bottom type" with no values. The bottom type is isomorphic with \[\bot\]. The bottom type is a type with no values and \[\bot\] is a proposition with no proofs.

Implication as function types

In logic, \[A \to B\] means "\[A\] implies \[B\]." If you have a proof of \[A \to B\] and a proof of \[A\], you can combine them to get a proof of \[B\]. In some functional programming languages like Haskell, A -> B is a function type that takes a parameter of type A and returns a parameter of type B . If you have a value with type A and have a function of type A -> B , you can call the function with the A -typed value as an argument to get a value of type B . Notice a pattern 🤔? Logical implication is isomorphic with function types.

Universal quantification as function types

Forall

In logic, \[\forall x \in S, P(x)\] represents universal quantification over \[x\]. It is a proposition that states for all values of \[x\] in a set \[S\], the proposition \[P(x)\] is true. For example, \[\forall n \in \mathbb N, n >= 0\] means that all natural numbers are at least zero. The proposition \[\forall P \in \text{PROP}, P \to P\] means that all propositions imply themselves.

Forall and implication

As it turns out, the propositions \[\forall x \in A, B\] and \[A \to B\] are identical. For both of them, a proof of \[A\] can be used to get a proof of \[B\]. In the first version, the proof of \[A\] is given the name \[x\]. This means that \[B\] can depend on \[x\]. \[B\] can be a function of \[x\]. This means that logical implication (\[\to\]) is a subset of forall (\[\forall\]). A forall can be replaced with an implication when \[B\] doesn't depend on \[x\].

Forall and function types

Just like implication, forall is also isomorphic with function types. A proposition \[\forall x \in A, B\] is isomorphic with the type of a function that takes a parameter \[x\] of type \[A\] and returns a parameter of type \[B\]. Because \[B\] can depend on \[x\], the function's return type \[B\] can depend on the value of its parameter \[x\]. This is where the name "dependent type" comes from. Forall (when considered a type instead of a proposition) is a dependent type, since a type depends on a value.

Are arrays dependent types?

Certain languages like Java and Rust have array types where the length of the array is part of the type. Examples are char[10] in Java and [char; 10] in Rust. While these arrays could be considered dependent types because a type depends on a value, this isn't general enough to be isomorphic with forall.

Other logical constructs

Type systems with these features are enough to be isomorphic with higher-order logic. There are other logical constructs, like biconditional \[\leftrightarrow\], not \[\lnot\], and \[\land\], or \[\lor\], exists \[\exists\]. These can be implemented with the tools above.

Implementing other logical constructs with the tools above

Not

\[\lnot A \equiv A \to \bot\]

And

\[A \land B \equiv \forall C \in \text{PROP}, (A \to B \to C) \to C\]

Biconditional

\[A \leftrightarrow B \equiv (A \to B) \land (B \to A)\]

Or

\[A \lor B \equiv \forall C \in \text{PROP}, (A \to C) \land (B \to C) \to C\]

Exists

\[\exists x \in A, B \equiv \lnot \forall x \in A, \lnot B\]

QED