119 lines
4.4 KiB
Plaintext
119 lines
4.4 KiB
Plaintext
;;;; 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))
|
|
|
|
;; The shorthand: a lone .field with no name before it binds a local of the
|
|
;; field's own name, which is what :keys does and in the spelling the rest of
|
|
;; the language uses for a field. It mixes with the pair form in one brace,
|
|
;; because the two are read one item at a time and a dot in head position is
|
|
;; the only thing that tells them apart.
|
|
(let [{.x .y} (Point {.x 30 .y 40})]
|
|
(show2 "shorthand" x y))
|
|
(let [{.x b .y} (Point {.x 50 .y 60})]
|
|
(show2 "shorthand-mixed" x 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)
|