Functions & Closures
The same small task in every language: define a named function square, write a closure factory make_adder(n) that captures n and returns a new function, then map a list through both — squaring each element, then adding 5. Watch for how a closure captures n (does it need an explicit return? a named lambda keyword? an arrow?), how anonymous functions read ({ ... } topic blocks vs. lambda/fun/|x|/sub {}), and how functions compose over a list (map with a lambda vs. a comprehension).
“Easy things should be easy, and hard things should be possible.” — Larry Wall
# A named function: the expression form (single expression, `= expr`)
sub square($x: Int): Int = $x * $x
# A closure factory: returns an anonymous sub that captures $n
sub make_adder($n: Int) {
sub($x: Int) { $x + $n }
}
sub main(): Int {
$add5 = make_adder(5)
@nums = [1, 2, 3, 4]
# `{ $_.square() }` is a topic-block lambda; $_ is its sole argument
@out = @nums.map({ $_.square() }).map({ $add5($_) })
print("squared+5: { @out }")
print("add5(10) = { $add5(10) }")
0
}Functions are first-class values, so make_adder simply returns an anonymous sub that closes over the immutable $n. guji has two anonymous forms: the { ... } topic block (one implicit argument $_) used here for map, and the explicit sub($x) { ... }. Thanks to data-first uniform call syntax, $_.square() is exactly square($_), so calls chain left-to-right with .. The expression form sub square(...) = ... is the idiomatic shape for one-liner functions.
package main
import "fmt"
func square(x int) int { return x * x }
// A closure factory: the returned func captures n.
func makeAdder(n int) func(int) int {
return func(x int) int { return x + n }
}
func main() {
add5 := makeAdder(5)
nums := []int{1, 2, 3, 4}
out := make([]int, len(nums))
for i, v := range nums {
out[i] = add5(square(v))
}
fmt.Println("squared+5:", out)
fmt.Println("add5(10) =", add5(10))
}Go has true closures: the function literal returned by makeAdder captures n by reference. Function types are spelled out explicitly (func(int) int), which is the price of Go's lack of inference for return types. Go has no map/comprehension in the standard library idiom, so the transformation is an explicit for ... range loop building a preallocated slice — the conventional Go style.
let square x = x * x
(* A closure factory: the returned function captures n. *)
let make_adder n = fun x -> x + n
let () =
let add5 = make_adder 5 in
let nums = [1; 2; 3; 4] in
let out = List.map (fun x -> add5 (square x)) nums in
Printf.printf "squared+5: [%s]\n"
(String.concat "; " (List.map string_of_int out));
Printf.printf "add5(10) = %d\n" (add5 10)Every function in OCaml is curried and a first-class value, so make_adder n returning fun x -> x + n is the natural way to build a closure — make_adder could even be written let make_adder n x = x + n and partially applied. Types are fully inferred (square : int -> int) with no annotations. List.map takes the function first and the list second, the opposite order from the data-first languages.
use strict;
use warnings;
sub square { my ($x) = @_; $x * $x }
# A closure factory: the returned sub captures $n.
sub make_adder {
my ($n) = @_;
return sub { my ($x) = @_; $x + $n };
}
my $add5 = make_adder(5);
my @nums = (1, 2, 3, 4);
my @out = map { $add5->(square($_)) } @nums;
print "squared+5: [", join(", ", @out), "]\n";
print "add5(10) = ", $add5->(10), "\n";An anonymous sub { ... } closes over the lexical my $n, which is exactly how Perl closures work. Arguments arrive in @_ and are unpacked with my ($x) = @_; a code reference is invoked with the ->() arrow. Perl's map { ... } @list runs its block with each element bound to the topic variable $_ — the same topic idea guji spells $_ inside { ... }.
sub square(Int $x --> Int) { $x * $x }
# A closure factory: the returned block captures $n.
sub make-adder(Int $n) {
-> Int $x { $x + $n }
}
my &add5 = make-adder(5);
my @nums = 1, 2, 3, 4;
my @out = @nums.map({ add5 square $_ });
say "squared+5: @out[]";
say "add5(10) = { add5(10) }";Raku makes the closure a pointy block -> Int $x { ... }, stored in a &-sigilled variable so it calls like a sub. The .map({ ... }) block uses the implicit topic $_ — the construct guji borrowed for its topic-block lambdas. Sigils and the optional gradual types (Int, --> Int) make Raku the most visible ancestor of guji's $/@/% and annotation style.
fn square(x: i32) -> i32 {
x * x
}
// A closure factory: `move` captures n into the returned closure.
fn make_adder(n: i32) -> impl Fn(i32) -> i32 {
move |x| x + n
}
fn main() {
let add5 = make_adder(5);
let nums = [1, 2, 3, 4];
let out: Vec<i32> = nums.iter().map(|&x| add5(square(x))).collect();
println!("squared+5: {:?}", out);
println!("add5(10) = {}", add5(10));
}Rust closures are |x| ...; returning one needs impl Fn(i32) -> i32 and a move so the closure owns the captured n rather than borrowing it. Transformation is lazy and iterator-based: nums.iter().map(...).collect() only runs when collected into a Vec. Like guji and Perl, the closure is passed to map data-first on the iterator, but the explicit Fn trait bound reflects Rust's no-GC ownership model.
def square(x):
return x * x
def make_adder(n):
return lambda x: x + n # closure capturing n
add5 = make_adder(5)
nums = [1, 2, 3, 4]
out = [add5(square(x)) for x in nums]
print(f"squared+5: {out}")
print(f"add5(10) = {add5(10)}")Python closures are everyday: make_adder returns a lambda that captures the enclosing n. lambda is limited to a single expression, so anything larger becomes a nested def. Rather than map, the idiomatic transformation is a list comprehension [add5(square(x)) for x in nums], which most Python programmers find clearer than map(...).