Closures
A closure is a function that captures variables from the scope where it was created and carries them along after that scope is gone. The shared task here is make_adder(n), which returns a new function that remembers n and adds it to its argument - call make_adder(10) and apply the result to 5 to get 15. Notice how every BEAM language treats functions as first-class values you can build at runtime and return; the only real difference is the syntax for an anonymous function (fun ... end, fn ->, fn(x) {}, lambda, or Lua's function) and how each one is later applied.
-module(adder).
-export([make_adder/1, demo/0]).
%% Returns an anonymous fun that closes over N.
make_adder(N) ->
fun(X) -> X + N end.
demo() ->
Add10 = make_adder(10),
%% Apply the captured fun to 5.
Add10(5).
%% => 15
Erlang builds the closure with fun(X) -> X + N end, which captures the surrounding N; binding it to Add10 lets you apply it like any function with Add10(5).
defmodule Adder do
# Returns an anonymous function that closes over n.
def make_adder(n) do
fn x -> x + n end
end
end
add10 = Adder.make_adder(10)
# Anonymous functions are applied with a dot: add10.(5)
add10.(5)
# => 15
Elixir's fn x -> x + n end captures n from the enclosing function, and because it is an anonymous value it is invoked with the dot-call syntax add10.(5).
import gleam/io
import gleam/int
// Returns a typed function value that closes over n.
pub fn make_adder(n: Int) -> fn(Int) -> Int {
fn(x) { x + n }
}
pub fn main() {
let add10 = make_adder(10)
// Applied like a normal function; the type fn(Int) -> Int is checked.
io.println(int.to_string(add10(5)))
// prints "15"
}
Gleam types the closure explicitly as fn(Int) -> Int, so the captured n and the returned function are checked at compile time; the result is applied with ordinary call syntax add10(5).
(defmodule adder
(export (make-adder 1) (demo 0)))
;; lambda captures n from the surrounding scope.
(defun make-adder (n)
(lambda (x) (+ x n)))
(defun demo ()
(let ((add10 (make-adder 10)))
;; funcall applies the closure to 5.
(funcall add10 5)))
;; => 15
LFE creates the closure with (lambda (x) (+ x n)), which captures n, and because the result is held in a variable it is applied with funcall rather than a plain call.
-- The inner function closes over the upvalue n.
local function make_adder(n)
return function(x)
return x + n
end
end
local add10 = make_adder(10)
-- Closures are called like any other function.
print(add10(5))
-- => 15
Lua closures capture surrounding locals as upvalues, so the inner function(x) keeps n alive after make_adder returns; add10(5) then applies it directly.