Last active
July 22, 2024 18:47
-
-
Save sgoguen/c2564ce40d95c65ab7ac08706ed6529a to your computer and use it in GitHub Desktop.
Converting GADTs with OOP Examples to TypeScript
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This is a fun experiement where we try reducing the amount of boilerplate code | |
// to implement the more verbose types in gadt.ts | |
abstract class Exp<T> { | |
abstract eval(): T; | |
} | |
/// Let's create a function that create a class that extends Exp | |
function createExp<TInput, T>(evalFn: (input: TInput) => T) { | |
return class extends Exp<T> { | |
constructor(private input: TInput) { | |
super(); | |
} | |
eval(): T { | |
return evalFn(this.input); | |
} | |
} | |
} | |
/// Each of the following statements is the corresponds to defining a new class that extends Exp<T> | |
const Lit = createExp((n: number) => n); | |
const StringLit = createExp((s: string) => s); | |
const Plus = createExp((e: { left: Exp<number>, right: Exp<number> }) => e.left.eval() + e.right.eval()); | |
const Equals = createExp((e: { left: Exp<number>, right: Exp<number> }) => e.left.eval() === e.right.eval()); | |
/// The following definitions aren't classes, but generic functions (with no arguments) that returns a class. | |
/// It should probably be memoized. | |
const Cond = <A>() => createExp((e: { cond: Exp<boolean>, left: Exp<A>, right: Exp<A> }) => e.cond.eval() ? e.left.eval() : e.right.eval()); | |
const Tuple = <A, B>() => createExp((e: { left: Exp<A>, right: Exp<B> }): [A, B] => [e.left.eval(), e.right.eval()]); | |
const Fst = <A, B>() => createExp((t: Exp<[A, B]>) => t.eval()[0]); | |
const Snd = <A, B>() => createExp((t: Exp<[A, B]>) => t.eval()[1]); | |
/// You may want to define helper functions to make creating instances of these classes easier | |
/// and to reduce the amount of boilerplate code | |
const lit = (value: number) => new Lit(value); | |
const plus = (left: Exp<number>, right: Exp<number>) => new Plus({ left, right }); | |
const equals = (left: Exp<number>, right: Exp<number>): Exp<boolean> => new Equals({ left, right }); | |
const cond = <A>(cond: Exp<boolean>, left: Exp<A>, right: Exp<A>): Exp<A> => (new (Cond<A>())({ cond, left, right })); | |
const tuple = <A, B>(left: Exp<A>, right: Exp<B>): Exp<[A, B]> => new (Tuple<A, B>())({ left, right }); | |
const fst = <A, B>(tuple: Exp<[A, B]>): Exp<A> => new (Fst<A, B>())(tuple); | |
const snd = <A, B>(tuple: Exp<[A, B]>): Exp<B> => new (Snd<A, B>())(tuple); | |
const ex1: Exp<boolean> = equals(lit(1), lit(2)); | |
const lit1: Exp<number> = lit(1); | |
const tup1: Exp<[boolean, number]> = tuple(ex1, lit1); | |
const ex2: Exp<boolean> = fst(tup1); | |
const ex3: Exp<number> = snd(tup1); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(* OCaml supports GADTs as of version 4.0 *) | |
type _ term = | |
| Lit : int -> int term | |
| Inc : int term -> int term | |
| IsZ : int term -> bool term | |
| If : bool term * 'a term * 'a term -> 'a term | |
| Pair : 'a term * 'b term -> ('a * 'b) term | |
| Fst : ('a * 'b) term -> 'a term | |
| Snd : ('a * 'b) term -> 'b term | |
let rec eval : type a. a term -> a = function | |
| Lit i -> i | |
| Inc t -> eval t + 1 | |
| IsZ t -> eval t = 0 | |
| If(b,t,e) -> if eval b then eval t else eval e | |
| Pair(a,b) -> (eval a, eval b) | |
| Fst t -> fst (eval t) | |
| Snd t -> snd (eval t) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This is a simple translation of Andrew Kennedy's GADT's | |
// | |
// data Exp t where | |
// Lit :: Int → Exp Int | |
// Plus :: Exp Int → Exp Int → Exp Int | |
// Equals :: Exp Int → Exp Int → Exp Bool | |
// Cond :: Exp Bool → Exp a → Exp a → Exp a | |
// Tuple :: Exp a → Exp b → Exp (a, b) | |
// Fst :: Exp (a, b) → Exp a | |
abstract class Exp<T> { | |
abstract eval(): T; | |
} | |
class Lit extends Exp<number> { | |
constructor(private value: number) { | |
super(); | |
} | |
eval() { | |
return this.value; | |
} | |
} | |
class Plus extends Exp<number> { | |
constructor(private left: Exp<number>, private right: Exp<number>) { | |
super(); | |
} | |
eval() { | |
return this.left.eval() + this.right.eval(); | |
} | |
} | |
class Equals extends Exp<boolean> { | |
constructor(private left: Exp<number>, private right: Exp<number>) { | |
super(); | |
} | |
eval() { | |
return this.left.eval() === this.right.eval(); | |
} | |
} | |
class Cond<A> extends Exp<A> { | |
constructor(private cond: Exp<boolean>, private left: Exp<A>, private right: Exp<A>) { | |
super(); | |
} | |
eval() { | |
return this.cond.eval() ? this.left.eval() : this.right.eval(); | |
} | |
} | |
class Tuple<A, B> extends Exp<[A, B]> { | |
constructor(private left: Exp<A>, private right: Exp<B>) { | |
super(); | |
} | |
eval(): [A, B] { | |
return [this.left.eval(), this.right.eval()]; | |
} | |
} | |
class Fst<A, B> extends Exp<A> { | |
constructor(private tuple: Exp<[A, B]>) { | |
super(); | |
} | |
eval(): A { | |
return this.tuple[0].eval(); | |
} | |
} | |
class Snd<A, B> extends Exp<B> { | |
constructor(private tuple: Exp<[A, B]>) { | |
super(); | |
} | |
eval(): B { | |
return this.tuple[1].eval(); | |
} | |
} | |
const lit = (value: number) => new Lit(value); | |
const plus = (left: Exp<number>, right: Exp<number>) => new Plus(left, right); | |
const equals = (left: Exp<number>, right: Exp<number>): Exp<boolean> => new Equals(left, right); | |
const cond = <A>(cond: Exp<boolean>, left: Exp<A>, right: Exp<A>): Exp<A> => new Cond(cond, left, right); | |
const tuple = <A, B>(left: Exp<A>, right: Exp<B>): Exp<[A, B]> => new Tuple(left, right); | |
const fst = <A, B>(tuple: Exp<[A, B]>): Exp<A> => new Fst(tuple); | |
const snd = <A, B>(tuple: Exp<[A, B]>): Exp<B> => new Snd(tuple); | |
const ex1: Exp<boolean> = equals(lit(1), lit(2)); | |
const lit1: Exp<number> = lit(1); | |
const tup1: Exp<[boolean, number]> = tuple(ex1, lit1); | |
const ex2: Exp<boolean> = fst(tup1); | |
const ex3: Exp<number> = snd(tup1); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment