Closure and Environment

Download Report

Transcript Closure and Environment

Closure and Environment
Compiler
Baojian Hua
[email protected]
Higher-order functions (HOF)

Higher-order functions are not just for call



can be passed as arguments
can be returned as results
can be stored in data structures


If functions don’t nest, then the
implementation is simple



objects! we’d discuss later
a simple code address
e.g., the “function pointer” in C
What about nesting with HOF?
Nesting
(* ML code. *)
(* Staging. Harper, section 11.5: *)
val add = fn x => (fn y => x + y)
val inc = add 1 (* fn y => 1 + y *)
(* Can be
val bop =
val add =
val inc =
more abstract: *)
fn f => (fn x => (fn y => f (x, y)))
fn (op +)
add 1
Nesting
// C code.
// GNU C extension supports this. But its
// implementation is rather limited and incorrect!
int (*(add)(int x))(int)
{
int f (int y)
{
return x + y;
}
return f;
}
// Don’t expect GCC behaves normally, hummmm…
int (*inc) (int) = add (1);
Moral


Nested HOL is a key feature in modern
functional programming languages
And now has grown into other language


e.g., C# and Java7
Key observation: function arguments and
locals can NOT be reclaimed after the call


fn x => (fn y => x + y)
they may be used in the returned nested function!
Closure



A closure is a data structure to
represent a function at runtime
A closure is essentially a heap-allocated
struct/tuple containing a code pointer,
as well as a (L-)values for the function’s
free variables (environment)
The process of converting a function to
a closure is called closure conversion
Lambda again
e -> n
-> x
-> \x.e
-> e e
// or in ML:
datatype e
= Int of int
| Var of string
| Lam of string * e
| App of e * e
Rules
C (n) = n
C (x) = #x env
C (\x.e) =
let fun f (env_t, x) =
let val x1 = #x1 env_t
…
val xn = #xn env_t
val env’ = {x=x, x1=x1, …, xn=xn}
in C (e)
end
in (f, env)
end
C (e1 e2) = C(e1) C(e2)
Example #1
// for code:
\x.x
// the initial call:
C (\x.x) =
let fun f (env_t, x) =
let val env’ = {x = x}
in C (x)
end
in (f, env)
end
Recursive transformation
// for code:
\x.x
// the initial call:
C (\x.x) =
let fun f (env_t, x) =
let val env = {x = x}
in #x env
end
in (f, env)
end
Hoisting
// for code:
\x.x
// hoist all code to top-level:
fun f (env_t, x) =
let val env = {x = x}
in #x env
end
(f, {})
Function call
// consider the code:
(\x.x) 5
// \x.x as before:
fun f (env_t, x) =
let val env’ = {x = x}
in #x env
end
(f, env)
// Leave it to you to verify the whole becomes:
(f, env) 5
// and the call becomes:
f (env, 5)
Summary so far

Three steps in closure conversion:

apply the conversion rules to make every
function closed


Hoisting:



a function become a closure: (code, env)
all functions at top-level
like those in C
function call become closure application

(code, env) x ==> code (env, x)
Example #2
// code:
\x.\y.x+y
// conversion:
C (\x.\y.x+y) =
let fun f1 (env_t, x) =
let val env = {x = x}
in C (\y.x+y)
end
in (f1, env)
end
// Leave to you to finish other steps!
Example #2
// code:
\x.\y.x+y
// final result:
fun f2 (ent_t, y) =
let val x = #x ent_t
val env = {x=x, y=y}
in #x env + #y env
end
fun f1 (env_t, x) =
let val env = {x = x}
in (f2, env)
end
(f1, env)
In picture
// for \x.\y.\z.x+y+z
f1
f2
f3
/\
Linked vs flatten closure

The flatten model of closure is bad:



it evolves too much “copy” operations
it’s space inefficient
Instead, we could make the closure
linked by sharing environment

just as the static link
Linked Environment
// revised rules:
C (\x.e) =
let fun f (env_t, x) =
let val env = Cons (env_t, x)
in C (e)
end
in (f, env)
end
In picture
// for \x.\y.\z.x+y+z
f1
f2
f3
/\