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
/\