an illustration in 5 languages
abstract class JSONBlob
class JSONString extends JSONBlob {
JSONString(String s) {...}
}class JSONNumber extends JSONBlob {
JSONNumber(Number n) {...}
}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"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)
(* 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"
OCaml (1996) (an ML family 1973)
// 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")
}Scala (2004) (an Java family 1995)
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");
};
}
}Java (since 1995,
but feature here added in 2021 (JDK 16))
#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;
}
C++ (1983, but pattern matching still not standard, alternatives shown!)
data 𝔹 : Set where
↑ : 𝔹 -- Voltage up
↓ : 𝔹 -- Voltage down
type bit =
| zero
| onesealed trait Bit
case Zero extends Bit
case One extends Bit
enum Bit {
ZERO, ONE;
}
abstract sealed class Bit {}
final class Zero() extends Bit {}
final class One() extends Bit {}
public sealed class Bit
permits Zero, One {
...
}
{
}
not : Bit -> Bit
not zero = one
not one = zeronot b = match b with
| zero -> one
| one -> zerodef not( b : Bit ) : Bit =
b match {
case Zero() => One()
case One() => Zero()
} select(b) {
case Bit.ZERO -> Bit.ONE
case Bit.ONE -> Bit.ZERO
}
if ( b instanceof Zero )
return new One();
if ( b instanceof One )
return new Zero();Bit not( Bit b ) {
return select( b ) {
case Zero() -> new One();
case One() -> new Zero();
}
}data List A where
nil : List A
cons : (a : A) -> (tail : List A) -> List Atype 'a list =
| Nil
| Cons of 'a * 'a listsealed trait List[A+]
case Nil extends List[Any]
case Cons( a : A, tail : List[A]) extends List[A]
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 )