;;;; Destructuring in a let: Clojure's binding forms over Flan's shapes. ;;;; ;;;; A struct stands in for Clojure's map, so {:keys [x y]} and {inner :field} ;;;; read fields off one; a fixed array stands in for its sequence, so [a b] ;;;; and [a b & rest] read elements out of one. None of it is a new form: it ;;;; all desugars in parse.ml into the Let, (.field x), at and slice that were ;;;; already there, which is why this program is the test that it works — the ;;;; typed IR has nothing in it a pattern could be hiding in. ;;;; ;;;; The case that matters most here is `calls`. A pattern binds several names ;;;; from one value, and that value is bound to a temporary *first*, so a ;;;; pattern over a call calls it once. Delete the temporary and every name ;;;; re-evaluates the initialiser: this program prints the call count, so that ;;;; mistake changes the output instead of hiding in it. (defstruct Point [x i32 y i32]) (defstruct Line [a Point b Point]) (defvar calls i32) (defn make-point [] Point (set calls (+ calls 1)) (Point {.x 3 .y 4})) (defn show2 [label string a i32 b i32] (print label) (print " ") (print a) (print " ") (print b) (println "")) (defn main [] i32 ;; :keys, the common case: one name per field, spelled as the field is. (let [{:keys [x y]} (Point {.x 1 .y 2})] (show2 "keys" x y)) ;; The pair form, which is what renames and what nests — a :keys entry is a ;; field name and never a pattern. (let [{a .x b .y} (Point {.x 10 .y 20})] (show2 "pairs" a b)) (let [l (Line {.a (Point {.x 5 .y 6}) .b (Point {.x 7 .y 8})})] (let [{{:keys [x y]} .b} l] (show2 "nested" x y)) ;; A pattern may shadow the very name it destructures, because the value is ;; read into a temporary before any of the names are bound. (let [{l .a} l] (show2 "shadow" (.x l) (.y l)))) ;; A later binding sees an earlier pattern's names, as in any let. (let [{:keys [x]} (Point {.x 100 .y 0}) doubled (* x 2)] (show2 "sequential" x doubled)) ;; A fixed array names every element. The count is checked against the type, ;; so [a b] over a [3 i32] is a compile error and not a silent prefix. (let [xs [11 22 33] [a b c] xs] (print "array ") (print a) (print " ") (print b) (print " ") (print c) (println "")) ;; & rest is the tail as a slice, which is an ordinary (slice xs n (len xs)) ;; over a local — nothing new, and nothing that outlives the array. (let [xs [1 2 3 4 5] [head & tail] xs] (print "rest ") (print head) (print " ") (print (len tail)) (print " ") (print (at tail 0)) (print " ") (print (at tail 3)) (println "")) ;; The tail may be empty: naming every element and then asking for the rest ;; is a zero-length slice, not an error. (let [xs [9 8] [p q & rest] xs] (print "empty-tail ") (print (+ p q)) (print " ") (print (len rest)) (println "")) ;; Patterns nest through each other: a struct inside an array. (let [ps [(Point {.x 1 .y 2}) (Point {.x 3 .y 4})] [{:keys [x]} {y .y}] ps] (show2 "nested-in-array" x y)) ;; A tail of something wider than a machine word. The corpus slices arrays of ;; i32, u8 and f32 and nothing else, so this is the one place the desugared ;; (slice xs n (len xs)) has to get a struct's stride right rather than a ;; scalar's. (let [ps [(Point {.x 1 .y 2}) (Point {.x 3 .y 4}) (Point {.x 5 .y 6})] [first & others] ps] (print "struct-tail ") (print (.x first)) (print " ") (print (len others)) (print " ") (print (.y (at others 0))) (print " ") (print (.x (at others 1))) (println "")) ;; Evaluate-once. Two patterns, two calls, four names — one call per pattern. ;; Without the temporary each of the four names would call it again: 4, not 2. (let [{:keys [x y]} (make-point) {a .x b .y} (make-point)] (print "calls ") (print calls) (print " ") (print (+ x (+ y (+ a b)))) (println "")) 0)