val times = fn a => (fn b => a*b);This declaration introduces
times
.
I will write the value as a closure:
times = [fn a => (fn b => a*b) | global]The bracketed notation has two parts: the code for the function (it is actually compiled) and the nonlocal referencing environment in which it is to be run (in this case global).
val twice = times 2;When
times
is called, it gets a new activation record. I will write that
activation record like this:
α: <times | global | a=2>The first part names the compiled code that is running. The second part is the nonlocal referencing environment in which that code runs. The third part is the binding of the formal parameters. I label the entire activation record with a greek name (
α
) so I can
refer to it later.
The body of times
needs to return a
function. It does so, returning this closure to be bound to twice
:
twice = [fn b => a*b | α]The returned closure has a nonlocal referencing environment
α
, which is
the activation record of the function doing
the returning.
So α
must not be deallocated when times
returns.
We will assume a garbage collector will eventually deallocate unused activation
records; we will never explicitly deallocate them.
val compose = fn (f,g) => (fn x => f(g(x)));The resulting closure:
compose = [fn (f,g) => (fn x => f(g(x))) | global]
val fourtimes = compose(twice, twice);This declaration invokes
compose
,
generating this activation record:
β: <compose | global | f=twice, g=twice>
Compose
then returns a value to be bound to fourtimes
:
fourtimes = [fn x => f(g(x)) | β]Both
f
and g
are compiled as references to 1-level nonlocal
values.
We now invoke fourtimes
:
fourtimes 5;This invocation builds a new activation record:
γ: <fourtimes | β | x=5>The body of
fourtimes
first needs to evaluate g(x)
.
The compiled code for fourtimes
knows that
g
is one level nonlocal; it is found in β
to be twice
.
So we invoke g=twice
in this new activation record:
δ: <twice | α | b=5>The body of
twice
is a*b
, where b
is local (value 5
),
and a
is 1-level nonlocal, therefore in α
(value 2
). So
the value 2*5=10
is returned.
We are back in γ
, and the body of fourtimes
now applies f
to the 10
that it just calculated. It finds f
one level nonlocal,
in β
; f
is twice
. Calling f=twice
builds this
activation record:
ε: <twice | α | b=10>Once more,
twice
evaluates a*b
where a
is the nonlocal value
2 (in α
) and b
is the local value 10
(in
ε
). This call returns 20
back to γ
, which returns
the 20
back to its caller, the main program.
val curry = fn f => (fn a => (fn b => f(a,b)));This declaration generates a closure:
curry = [fn f => (fn a => (fn b => f(a,b))) | global]
val curryPlus = curry plus;Function
curry
is called in activation record γ
:
γ: <curry | global | f=plus>This invocation returns the following closure, bound to
curryPlus
:
curryPlus = [fn a => (fn b => f(a,b)) | γ]
val successor = curryPlus 1;This declaration invokes
curryPlus
, generating this activation record:
ι: <curryPlus | γ | a=1>This call returns the following closure, bound to
successor
:
successor = [fn b => f(a,b) | ι]In this closure,
a
is one level nonlocal, b
is local, and f
is two levels nonlocal.
We can now invoke successor
:
successor 3;We build a new activation record:
kappa: <successor | ι | b=3>Local value:
b=3
. One level nonlocal (ι
): a=1
.
Two levels nonlocal (γ
): f=plus
.
The body of successor
needs to invoke f(a,b)
, which is resolved as
plus(1,3)
, which yields 4
.