A compact cheat sheet of common SML keywords you’ll see in the lambda-calculus SML code, with short explanations and rough Python parallels.
What it does: Binds a value to a name (like a variable or constant).
(* SML *)
val x = 3;
val y = x + 2;
# Python
x = 3
y = x + 2
What it does: Defines a named function, often with pattern matching on arguments.
(* SML *)
fun add (a, b) = a + b;
fun fact 0 = 1
| fact n = n * fact (n - 1);
# Python
def add(a, b):
return a + b
def fact(n):
if n == 0:
return 1
else:
return n * fact(n - 1)
What it does: Defines a new sum type with multiple constructors (used heavily for lambda terms).
(* SML *)
datatype LEXP =
ID of string
| LAM of string * LEXP
| APP of LEXP * LEXP;
# Python (roughly, using classes)
class ID:
def __init__(self, name):
self.name = name
class LAM:
def __init__(self, var, body):
self.var = var
self.body = body
class APP:
def __init__(self, f, arg):
self.f = f
self.arg = arg
What it does: Creates a new name for an existing type (a synonym, not a new type).
(* SML *)
type Var = string;
type Env = (Var * int) list;
# Python (no real type alias at runtime, but we can annotate)
Var = str
Env = list[tuple[Var, int]]
What it does: Creates an anonymous (lambda) function.
(* SML *)
val inc = fn x => x + 1;
val apply = fn (f, x) => f x;
# Python
inc = lambda x: x + 1
def apply(f, x):
return f(x)
What it does: Introduces local definitions and an expression that uses them.
(* SML *)
val result =
let
val x = 2
val y = 3
in
x * y
end;
# Python (using a block or inner function)
def compute():
x = 2
y = 3
return x * y
result = compute()
What it does: Standard conditional expression (must have an else branch).
(* SML *)
val sign =
if x > 0 then 1
else if x < 0 then ~1
else 0;
# Python
if x > 0:
sign = 1
elif x < 0:
sign = -1
else:
sign = 0
What it does: Matches a value against patterns (especially for datatypes).
(* SML *)
fun eval (ID x) = ...
| eval (LAM (x, e)) = ...
| eval (APP (e1, e2)) = ...;
(* or with case *)
fun eval e =
case e of
ID x => ...
| LAM (x, body) => ...
| APP (e1, e2) => ...;
# Python (manual pattern matching)
def eval(expr):
if isinstance(expr, ID):
...
elif isinstance(expr, LAM):
...
elif isinstance(expr, APP):
...
What it does: Used with fun or val to define recursive functions/values.
(* SML *)
fun fact 0 = 1
| fact n = n * fact (n - 1);
(* or explicitly *)
val rec fact =
fn 0 => 1
| n => n * fact (n - 1);
# Python
def fact(n):
if n == 0:
return 1
return n * fact(n - 1)
What they do: Short-circuit logical operators and negation.
(* SML *)
val b1 = (x > 0) andalso (y > 0);
val b2 = (x = 0) orelse (y = 0);
val b3 = not b1;
# Python
b1 = (x > 0) and (y > 0)
b2 = (x == 0) or (y == 0)
b3 = not b1
What it does: Declares a new exception type.
(* SML *)
exception EvalError;
exception UnboundVar of string;
# Python
class EvalError(Exception):
pass
class UnboundVar(Exception):
def __init__(self, name):
self.name = name
What it does: Raises an exception.
(* SML *)
raise EvalError;
raise UnboundVar "x";
# Python
raise EvalError()
raise UnboundVar("x")
What it does: Catches exceptions and provides alternative behavior.
(* SML *)
val result =
(eval e)
handle UnboundVar x => 0
| EvalError => ~1;
# Python
try:
result = eval(e)
except UnboundVar as ex:
result = 0
except EvalError:
result = -1
What they do: Define modules (structure), module interfaces (signature), and parameterized modules (functor).
(* SML *)
signature LEXP_SIG =
sig
datatype LEXP = ...
val eval : LEXP -> LEXP
end;
structure Lexp : LEXP_SIG =
struct
datatype LEXP = ...
fun eval e = ...
end;
# Python (rough analogy: modules and classes)
# In a file lexp.py
class LEXP:
...
def eval(expr):
...