(input) (output "Adders: 15 25" "Two captures: 47" "No captures: 7" "Through a parameter: 110" "From an array: 1 2 3" "Index evaluated once: 21 i 1" "Through a struct member: 8" "Nested: 33") (return 0) ;;; A closure is a code pointer beside its captures, so what this ;;; checks is that the captures survive the lifting -- that two ;;; closures of one shape keep their own environments, that a closure ;;; outlives the call that built it, and that calling one through a ;;; parameter, an array element or a struct member resolves the same ;;; way as through a local. ;;; ;;; A receiver is also an ordinary expression: `[table (++ i)]' has to ;;; evaluate its index exactly once, the way it would for an array of ;;; function pointers. (include stdio.h) (fn make-adder ((n int)) (closure ((int)) int) (return (closure ((b int)) int (n) (return (+ n b))))) (fn make-affine ((k int) (b int)) (closure ((int)) int) (return (closure ((x int)) int (k b) (return (+ (* k x) b))))) (fn make-const-7 () (closure () int) (return (closure () int () (return 7)))) ;;; A closure arriving as a parameter: its type is written, so the call ;;; resolves without knowing where it came from (fn apply-twice ((f (closure ((int)) int)) (x int)) int (return (f (f x)))) (struct handlers ((on-tick (closure ((int)) int)))) (pub fn main () int (var add-10 (closure ((int)) int) (make-adder 10)) (var add-20 (closure ((int)) int) (make-adder 20)) (printf "Adders: %d %d\n" (add-10 5) (add-20 5)) (var affine (closure ((int)) int) (make-affine 5 2)) (printf "Two captures: %d\n" (affine 9)) (var seven (closure () int) (make-const-7)) (printf "No captures: %d\n" (seven)) (printf "Through a parameter: %d\n" (apply-twice (make-adder 50) 10)) (var table (¤ (closure ((int)) int) 3)) (var i int 0) (for (= i 0) (< i 3) (++ i) (= (¤ table i) (make-adder i))) (printf "From an array: %d %d %d\n" ((¤ table 0) 1) ((¤ table 1) 1) ((¤ table 2) 1)) ;; the index must be evaluated once, so `i' ends at 1 and not 2 -- ;; read in a separate statement, since reading and bumping it in one ;; printf would be unsequenced whatever the closure did (= i 0) (var once int ([table (++ i)] 20)) (printf "Index evaluated once: %d i %d\n" once i) (var h (struct handlers) #((struct handlers) : .on-tick (make-adder 5))) (printf "Through a struct member: %d\n" ((. h on-tick) 3)) ;; a closure built inside a closure, capturing that one's capture (var outer (closure ((int)) int) (closure ((x int)) int () (var inner (closure ((int)) int) (make-adder x)) (return (inner 3)))) (printf "Nested: %d\n" (outer 30)) (return 0))