Induction with data
an illustration in 5 languages
2025 James B. Wilson

abstract class JSONBlob
class JSONString extends JSONBlob {
JSONString(String s) {...}
}class JSONNumber extends JSONBlob {
JSONNumber(Number n) {...}
}\(\vdash \mathbb{B}:\text{Set}\) (Form\(_{\mathbb{B}}\))
\(\vdash 0:\mathbb{B}\) (Intro\(_{0-\mathbb{B}}\))
\(\vdash 1:\mathbb{B}\) (Intro\(_{1-\mathbb{B}}\))
\(\begin{array}{rl} \Gamma & \vdash b:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} : P(b)\end{array}\) (Elim\(_{\mathbb{B}}\))
\(\begin{array}{rl} \Gamma & \vdash 0:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} := x\end{array}\) (0-Comp\(_{\mathbb{B}}\))
open import Data.Nat using (ℕ)
open import Data.String using (String)
open import Data.Sum using (_⊎_; inj₁; inj₂)
module Bits where
-- Binary digits up ↑ and down ↓
data 𝔹 : Set where
↑ : 𝔹
↓ : 𝔹
-- Use of a bit
asFive : 𝔹 → ℕ ⊎ String
asFive ↑ = inj₁ 5
asFive ↓ = inj₂ "Five"\(\begin{array}{rl} \Gamma & \vdash 1:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} := y\end{array}\) (1-Comp\(_{\mathbb{B}}\))
Agda (1999) (a Haskell family 1990)
open import Data.Nat using (ℕ)
open import Data.String using (String)
open import Data.Sum using (_⊎_; inj₁; inj₂)
module Bits where
-- Binary digits up ↑ and down ↓
data Trit : Set where
a : Trit
b : Trit
c : Trit
-- Do something
thing : Trit → ℕ ⊎ String
thing a = inj₁ 5
thing c = inj₂ "Five"
thing _ = inj₁ 42Agda (1999) (a Haskell family 1990)
\(\vdash \mathbb{B}:\text{Set}\) (Form\(_{\mathbb{B}}\))
\(\vdash 0:\mathbb{B}\) (Intro\(_{0-\mathbb{B}}\))
\(\vdash 1:\mathbb{B}\) (Intro\(_{1-\mathbb{B}}\))
\(\begin{array}{rl} \Gamma & \vdash b:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} : P(b)\end{array}\) (Elim\(_{\mathbb{B}}\))
\(\begin{array}{rl} \Gamma & \vdash 0:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} := x\end{array}\) (0-Comp\(_{\mathbb{B}}\))
(* Binary digits up and down *)
type bit =
| Up
| Down
(* Sum type for either natural number or string *)
type nat_or_string =
| Left of int
| Right of string
(* Use of a bit. *)
let asFive = function
| Up -> Left 5
| Down -> Right "Five"
\(\begin{array}{rl} \Gamma & \vdash 1:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} := y\end{array}\) (1-Comp\(_{\mathbb{B}}\))
OCaml (1996) (an ML family 1973)
\(\vdash \mathbb{B}:\text{Set}\) (Form\(_{\mathbb{B}}\))
\(\vdash 0:\mathbb{B}\) (Intro\(_{0-\mathbb{B}}\))
\(\vdash 1:\mathbb{B}\) (Intro\(_{1-\mathbb{B}}\))
\(\begin{array}{rl} \Gamma & \vdash b:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} : P(b)\end{array}\) (Elim\(_{\mathbb{B}}\))
\(\begin{array}{rl} \Gamma & \vdash 0:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} := x\end{array}\) (0-Comp\(_{\mathbb{B}}\))
// Binary digits up and down
sealed trait 𝔹
case object ↑ extends 𝔹
case object ↓ extends 𝔹
// Use of a bit
def asFive(bit: 𝔹): Either[Int, String] = bit match {
case ↑ => Left(5)
case ↓ => Right("Five")
}\(\begin{array}{rl} \Gamma & \vdash 1:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} := y\end{array}\) (1-Comp\(_{\mathbb{B}}\))
Scala (2004) (an Java family 1995)
\(\vdash \mathbb{B}:\text{Set}\) (Form\(_{\mathbb{B}}\))
\(\vdash 0:\mathbb{B}\) (Intro\(_{0-\mathbb{B}}\))
\(\vdash 1:\mathbb{B}\) (Intro\(_{1-\mathbb{B}}\))
\(\begin{array}{rl} \Gamma & \vdash b:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} : P(b)\end{array}\) (Elim\(_{\mathbb{B}}\))
\(\begin{array}{rl} \Gamma & \vdash 0:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} := x\end{array}\) (0-Comp\(_{\mathbb{B}}\))
import java.util.Objects;
class Bits {
// Binary digits up and down using sealed interface
sealed interface 𝔹 permits Up, Down {}
final class Up() implements 𝔹 {}
final class Down implements 𝔹 {}
// Sum type equivalent using sealed interface
sealed interface Either<L, R> permits Left, Right {}
record Left<L, R>(L value) implements Either<L, R> {}
record Right<L, R>(R value) implements Either<L, R> {}
// Use of a bit.
Either<Integer, String> asFive(𝔹 bit) {
return switch (bit) {
case Up up -> new Left<>(5);
case Down down -> new Right<>("Five");
};
}
}\(\begin{array}{rl} \Gamma & \vdash 1:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} := y\end{array}\) (1-Comp\(_{\mathbb{B}}\))
Java (since 1995,
but feature here added in 2021 (JDK 16))
\(\vdash \mathbb{B}:\text{Set}\) (Form\(_{\mathbb{B}}\))
\(\vdash 0:\mathbb{B}\) (Intro\(_{0-\mathbb{B}}\))
\(\vdash 1:\mathbb{B}\) (Intro\(_{1-\mathbb{B}}\))
\(\begin{array}{rl} \Gamma & \vdash b:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} : P(b)\end{array}\) (Elim\(_{\mathbb{B}}\))
\(\begin{array}{rl} \Gamma & \vdash 0:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} := x\end{array}\) (0-Comp\(_{\mathbb{B}}\))
#include <variant>
#include <string>
#include <iostream>
// Binary digits up and down
enum class 𝔹 {
↑, // up
↓ // down
};
// Sum type equivalent using std::variant
using EitherIntString = std::variant<int, std::string>;
// Use of a bit
EitherIntString asFive(𝔹 bit) {
switch (bit) {
case 𝔹::↑:
return 5;
case 𝔹::↓:
return std::string("Five");
}
// Should never reach here with complete switch
return 0;
}
\(\begin{array}{rl} \Gamma & \vdash 1:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} := y\end{array}\) (1-Comp\(_{\mathbb{B}}\))
C++ (1983, but pattern matching still not standard, alternatives shown!)
\(\vdash \mathbb{B}:\text{Set}\) (Form\(_{\mathbb{B}}\))
\(\vdash 0:\mathbb{B}\) (Intro\(_{0-\mathbb{B}}\))
\(\vdash 1:\mathbb{B}\) (Intro\(_{1-\mathbb{B}}\))
Logic: two independent possibilities.
data 𝔹 : Set where
↑ : 𝔹 -- Voltage up
↓ : 𝔹 -- Voltage down
Haskell Family (Agda, Idris)
type bit =
| zero
| oneML Family (OCaml)
sealed trait Bit
case Zero extends Bit
case One extends Bit
Scala
enum Bit {
ZERO, ONE;
}
Java (similar for C++)
abstract sealed class Bit {}
final class Zero() extends Bit {}
final class One() extends Bit {}
Java altneratives
public sealed class Bit
permits Zero, One {
...
}
\(\vdash \mathbb{B}:\text{Set}\) (Form\(_{\mathbb{B}}\))
\(\begin{array}{rl} \Gamma & \vdash b:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} : P(b)\end{array}\) (Elim\(_{\mathbb{B}}\))
{
}
\(\begin{array}{rl} \Gamma & \vdash 0:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} := x\end{array}\) (0-Comp\(_{\mathbb{B}}\))
\(\begin{array}{rl} \Gamma & \vdash 1:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} := y\end{array}\) (1-Comp\(_{\mathbb{B}}\))
\(\vdash 0:\mathbb{B}\) (Intro\(_{0-\mathbb{B}}\))
\(\vdash 1:\mathbb{B}\) (Intro\(_{1-\mathbb{B}}\))
not : Bit -> Bit
not zero = one
not one = zeroHaskell / Idris / Agda
not b = match b with
| zero -> one
| one -> zeroOCaml
def not( b : Bit ) : Bit =
b match {
case Zero() => One()
case One() => Zero()
} Scala
select(b) {
case Bit.ZERO -> Bit.ONE
case Bit.ONE -> Bit.ZERO
}
Java (similar for C++)
if ( b instanceof Zero )
return new One();
if ( b instanceof One )
return new Zero();Java altneratives
Bit not( Bit b ) {
return select( b ) {
case Zero() -> new One();
case One() -> new Zero();
}
}\(\begin{array}{rl} \Gamma & \vdash b:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} : P(b)\end{array}\) (Elim\(_{\mathbb{B}}\))
\(\begin{array}{rl} \Gamma & \vdash 0:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} = x\end{array}\) (0-Comp\(_{\mathbb{B}}\))
\(A:Set\vdash \text{List}[A]:\text{Set}\) (Form\(_{List}\))
\(\vdash []:List[A]\) (Intro\(_{List}\))
\(a:A, tail:List[A]\vdash a::tail:List[A]\) (Intro\(_{List}\))
Logic: Lists.
data List A where
nil : List A
cons : (a : A) -> (tail : List A) -> List AHaskell / Idris / Agda
type 'a list =
| Nil
| Cons of 'a * 'a listOCaml
sealed trait List[A+]
case Nil extends List[Any]
case Cons( a : A, tail : List[A]) extends List[A]
Scala
Java (similar for C++)
public sealed class List<A> {}
class final Nil() extends List<Class> {}
class final Cons(<A> a,
List<A> tail) extends List<A> {}
class JSON
case AsBoolean(b : Boolean)
case AsString(s : String)
case AsNumber(n : Number)
case AsArray(len : Int)( as : Vector[JSON, len ] )
case null
case AsObject( blob : JSON )
case UserTag(name : String)(blob : JSON)
case Append( head : JSON, tail : JSON )\begin{aligned}
b& :\mathbb{J}^0\to \mathbb{J}\\
s(x:\text{String})& :\mathbb{J}^0\to \mathbb{J}\\
n(x:\text{Number})& :\mathbb{J}^0\to \mathbb{J}\\
a_n & :\mathbb{J}^n\to \mathbb{J}\\
\text{null} & :\mathbb{J}^0\to \mathbb{J}\\
o &: \mathbb{J}^1\to \mathbb{J}\\
\text{tag}(\text{name}:\text{String}) & : \mathbb{J}\to \mathbb{J}\\
\text{Append} &:\mathbb{J}^2\to \mathbb{J}
\end{aligned}
Induction
By James Wilson
Induction
Inductions is reasoning about data mixed with operations on that data.
- 32