;;;; 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-str label) (print-str " ") (print-i64 (i64 a)) (print-str " ") (print-i64 (i64 b)) (newline)) (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-str "array ") (print-i64 (i64 a)) (print-str " ") (print-i64 (i64 b)) (print-str " ") (print-i64 (i64 c)) (newline)) ;; & 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-str "rest ") (print-i64 (i64 head)) (print-str " ") (print-i64 (i64 (len tail))) (print-str " ") (print-i64 (i64 (at tail 0))) (print-str " ") (print-i64 (i64 (at tail 3))) (newline)) ;; 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-str "empty-tail ") (print-i64 (i64 (+ p q))) (print-str " ") (print-i64 (i64 (len rest))) (newline)) ;; 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-str "struct-tail ") (print-i64 (i64 (.x first))) (print-str " ") (print-i64 (i64 (len others))) (print-str " ") (print-i64 (i64 (.y (at others 0)))) (print-str " ") (print-i64 (i64 (.x (at others 1)))) (newline)) ;; 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-str "calls ") (print-i64 (i64 calls)) (print-str " ") (print-i64 (i64 (+ x (+ y (+ a b))))) (newline)) 0)