Functions are an important part of Julia programming. They can be defined directly within modules, in which case the functions are referred to as top-level. But functions can also be defined within other functions. Such functions are called "closures".
Closures capture the variables in their outer function. A top-level function can only use global variables from their module, function parameters, or local variables:
x = 0 # global
function toplevel(y)
println("x = ", x, " is a global variable")
println("y = ", y, " is a parameter")
z = 2
println("z = ", z, " is a local variable")
end
A closure, on the other hand, can use all those in addition to variables from outer functions that it captures:
x = 0 # global
function toplevel(y)
println("x = ", x, " is a global variable")
println("y = ", y, " is a parameter")
z = 2
println("z = ", z, " is a local variable")
function closure(v)
println("v = ", v, " is a parameter")
w = 3
println("w = ", w, " is a local variable")
println("x = ", x, " is a global variable")
println("y = ", y, " is a closed variable (a parameter of the outer function)")
println("z = ", z, " is a closed variable (a local of the outer function)")
end
end
If we run c = toplevel(10)
, we see the result is
julia> c = toplevel(10)
x = 0 is a global variable
y = 10 is a parameter
z = 2 is a local variable
(::closure) (generic function with 1 method)
Note that the tail expression of this function is a function in itself; that is, a closure. We can call the closure c
like it was any other function:
julia> c(11)
v = 11 is a parameter
w = 3 is a local variable
x = 0 is a global variable
y = 10 is a closed variable (a parameter of the outer function)
z = 2 is a closed variable (a local of the outer function)
Note that c
still has access to the variables y
and z
from the toplevel
call — even though toplevel
has already returned! Each closure, even those returned by the same function, closes over different variables. We can call toplevel
again
julia> d = toplevel(20)
x = 0 is a global variable
y = 20 is a parameter
z = 2 is a local variable
(::closure) (generic function with 1 method)
julia> d(22)
v = 22 is a parameter
w = 3 is a local variable
x = 0 is a global variable
y = 20 is a closed variable (a parameter of the outer function)
z = 2 is a closed variable (a local of the outer function)
julia> c(22)
v = 22 is a parameter
w = 3 is a local variable
x = 0 is a global variable
y = 10 is a closed variable (a parameter of the outer function)
z = 2 is a closed variable (a local of the outer function)
Note that despite d
and c
having the same code, and being passed the same arguments, their output is different. They are distinct closures.