Skip to content

Instantly share code, notes, and snippets.

@dabrahams
Last active March 19, 2021 18:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dabrahams/e10434553803143ff4405223a7ddf451 to your computer and use it in GitHub Desktop.
Save dabrahams/e10434553803143ff4405223a7ddf451 to your computer and use it in GitHub Desktop.
From §2.1 of Sig Ager Biernacki Danvy & Midgaard: A Functional Correspondence Between Evaluators and Abstract Machines
(* Exactly what appears in the paper *)
datatype term = IND of int (* de Bruijn index *)
| ABS of term
| APP of term * term
structure Eval0
= struct
datatype denval = THUNK of unit -> expval
and expval = FUNCT of denval -> expval
(* eval : term * denval list -> expval *)
fun eval (IND n, e)
= let val (THUNK thunk) = List.nth (e, n)
in thunk ()
end
| eval (ABS t, e)
= FUNCT (fn v => eval (t, v :: e))
| eval (APP (t0, t1), e)
= let val (FUNCT f) = eval (t0, e)
in f (THUNK (fn () => eval (t1, e)))
end
(* main : term -> expval *)
fun main t = eval (t, nil)
end
// An unsafe translation that uses plain C unions, and
// doesn't show most of the problems with C++.
#include <utility>
#include <deque>
#include <functional>
struct term {
enum class Kind { IND, ABS, APP };
Kind kind;
union {
int IND; // de Bruijn index
term* ABS;
std::pair<term*, term*> APP;
};
};
struct expval;
struct denval { std::function<auto () -> expval> THUNK; };
struct expval { std::function<auto (denval) -> expval> FUNCT; };
template <class T> auto prepend(T e, std::deque<T> v) -> std::deque<T> {
v.push_front(e);
return v;
}
auto eval(term t, std::deque<denval> e) -> expval {
switch (t.kind) {
case term::Kind::IND:
return e[t.IND].THUNK();
case term::Kind::ABS: {
auto t0 = *t.ABS;
return { [=](denval v){ return eval(t0, prepend(v, e)); } };
}
case term::Kind::APP:
auto [t0, t1] = t.APP;
auto f = eval(*t0, e).FUNCT;
return f({ [=,t1=t1](){ return eval(*t1, e); } });
}
}
auto main1(term t) -> expval { return eval(t, std::deque<denval>()); }
auto main() -> int {}
// An extremely literal translation of what's in the paper
indirect enum term {
case IND(Int) // de Bruijn index
case ABS(term)
case APP(term, term)
}
enum denval { case THUNK(()->expval) }
enum expval { case FUNCT((denval)->expval) }
func eval(_ t: term, _ p: [denval]) -> expval {
switch (t, p) {
case let (.IND(n), e):
if case let .THUNK(thunk) = e[n] {
return thunk()
}
case let (.ABS(t0), e):
return .FUNCT({ v in eval(t0, [v] + e) })
case let (.APP(t0, t1), e):
if case let .FUNCT(f) = eval(t0, e) {
return f(.THUNK({ eval(t1, e) }))
}
}
fatalError("unreachable") // because we used if case to unwrap denval/expval.
}
func main(_ t: term) -> expval { eval(t, []) }
// Like swift1, but we unwrap denval and expval with a switch
indirect enum term {
case IND(Int) // de Bruijn index
case ABS(term)
case APP(term, term)
}
enum denval { case THUNK(()->expval) }
enum expval { case FUNCT((denval)->expval) }
func eval(_ t: term, _ p: [denval]) -> expval {
switch (t, p) {
case let (.IND(n), e):
switch e[n] { case let .THUNK(thunk):
return thunk()
}
case let (.ABS(t0), e):
return .FUNCT({ v in eval(t0, [v] + e) })
case let (.APP(t0, t1), e):
switch eval(t0, e) { case let .FUNCT(f):
return f(.THUNK({ eval(t1, e) }))
}
}
}
func main(_ t: term) -> expval { eval(t, []) }
// unary enums (denval/expval) are a bit awkward in Swift; use structs instead.
indirect enum term {
case IND(Int) // de Bruijn index
case ABS(term)
case APP(term, term)
}
struct denval { let THUNK: ()->expval }
struct expval { let FUNCT: (denval)->expval }
func eval(_ t: term, _ p: [denval]) -> expval {
switch (t, p) {
case let (.IND(n), e):
return e[n].THUNK()
case let (.ABS(t0), e):
return .init(FUNCT: { v in eval(t0, [v] + e) })
case let (.APP(t0, t1), e):
let f = eval(t0, e).FUNCT
return f(.init(THUNK: { eval(t1, e) }))
}
}
func main(_ t: term) -> expval { eval(t, []) }
// Eliminate needless pattern matching
indirect enum term {
case IND(Int) // de Bruijn index
case ABS(term)
case APP(term, term)
}
struct denval { let THUNK: ()->expval }
struct expval { let FUNCT: (denval)->expval }
func eval(_ t: term, _ e: [denval]) -> expval {
switch t {
case let .IND(n):
return e[n].THUNK()
case let .ABS(t0):
return .init(FUNCT: { v in eval(t0, [v] + e) })
case let .APP(t0, t1):
let f = eval(t0, e).FUNCT
return f(.init(THUNK: { eval(t1, e) }))
}
}
func main(_ t: term) -> expval { eval(t, []) }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment