diff options
| author | Andrey Listopadov <andreyorst@gmail.com> | 2022-10-31 14:24:33 +0300 |
|---|---|---|
| committer | Andrey Listopadov <andreyorst@gmail.com> | 2022-10-31 14:24:33 +0300 |
| commit | d5c73f5ee4a5b769c691b56adf80bb1c3c10f5ee (patch) | |
| tree | 761f8b31fbcc0effb46181e5832a3d05c1e5f6aa /init-macros.fnl | |
| parent | 44fa0c9514acade4b7dc80be1b1a1dcd5b3b0326 (diff) | |
update loop doc
Diffstat (limited to 'init-macros.fnl')
| -rw-r--r-- | init-macros.fnl | 98 |
1 files changed, 50 insertions, 48 deletions
diff --git a/init-macros.fnl b/init-macros.fnl index 0e45c77..0489f48 100644 --- a/init-macros.fnl +++ b/init-macros.fnl @@ -78,7 +78,7 @@ specified namespace. # Examples Creating several namespaces in the file, and defining functions in each: -```fennel +``` fennel (ns a) (defn f [] \"f from a\") (ns b) @@ -99,7 +99,7 @@ local bindings. In other words, when defining a local with `def`, a bot a local binding and a namespaced binding are created, and switching current namespace won't change the local binding: -``` +``` fennel :skip-test >> (ns foo) nil >> (def x 42) @@ -138,7 +138,7 @@ The `requirements` spec is a list that consists of vectors, specifying library name and a possible alias or a vector of names to refer to without a prefix: -``` +``` fennel :skip-test (ns some-namespace \"Description of the some-namespace.\" (:require [some.lib] @@ -150,7 +150,7 @@ without a prefix: Which is equivalent to: -``` +``` fennel :skip-test (local some-namespace {}) (local lib (require :some.lib)) (local lib2 (require :some.other.lib)) @@ -777,7 +777,7 @@ forms), and the values to bind to them. For example: -```fennel +``` fennel (loop [[first & rest] [1 2 3 4 5] i 0] (if (= nil first) @@ -794,7 +794,51 @@ time with the table lacking its head element (thus consuming one element per iteration), and with `i` being called with one value greater than the previous. When the loop terminates (When the user doesn't call `recur`) it will return the -number of elements in the passed in table. (In this case, 5)"} +number of elements in the passed in table. (In this case, 5) + +# Limitations + +In order to only evaluate expressions once and support sequential +bindings, the binding table has to be transformed like this: + +``` fennel :skip-test +(loop [[x & xs] (foo) + y (+ x 1)] + ...) + +(let [_1_ (foo) + [x & xs] _1_ + _2_ (+ x 1) + y _2_] + ((fn recur [[x & xs] y] ...) _1_ _2_) +``` + +This ensures that `foo` is called only once, its result is cached in a +`sym1#` binding, and that `y` can use the destructured value, obtained +from that binding. The value of this binding is later passed to the +function to begin the first iteration. + +This has two unfortunate consequences. One is that the initial +destructuring happens twice - first, to make sure that later bindings +can be properly initialized, and second, when the first looping +function call happens. Another one is that as a result, `loop` macro +can't work with multiple-value destructuring, because these can't be +cached as described above. E.g. this will not work: + +``` fennel :skip-test +(loop [(x y) (foo)] ...) +``` + +Because it would be transformed to: + +``` fennel :skip-test +(let [_1_ (foo) + (x y) _1_] + ((fn recur [(x y)] ...) _1_) +``` + +`x` is correctly set, but `y` is completely lost. Therefore, this +macro checks for lists in bindings."} (let [recur (sym :recur) keys [] gensyms [] @@ -805,48 +849,6 @@ number of elements in the passed in table. (In this case, 5)"} (let [key (. binding-vec (- i 1)) gs (gensym i)] (assert-compile (not (list? key)) "loop macro doesn't support multiple-value destructuring" key) - ;; In order to only evaluate expressions once and support sequential - ;; bindings, the binding table has to be transformed like this: - ;; - ;; ``` fennel - ;; (loop [[x & xs] (foo) - ;; y (+ x 1)] - ;; ...) - ;; - ;; (let [_1_ (foo) - ;; [x & xs] _1_ - ;; _2_ (+ x 1) - ;; y _2_] - ;; ((fn recur [[x & xs] y] ...) _1_ _2_) - ;; ``` - ;; - ;; This ensures that `foo` is called only once, its result is cached in a - ;; `sym1#` binding, and that `y` can use the destructured value, obtained - ;; from that binding. The value of this binding is later passed to the - ;; function to begin the first iteration. - ;; - ;; This has two unfortunate consequences. One is that the initial - ;; destructuring happens twice - first, to make sure that later bindings - ;; can be properly initialized, and second, when the first looping - ;; function call happens. Another one is that as a result, `loop` macro - ;; can't work with multiple-value destructuring, because these can't be - ;; cached as described above. E.g. this will not work: - ;; - ;; ``` fennel - ;; (loop [(x y) (foo)] ...) - ;; ``` - ;; - ;; Because it would be transformed to: - ;; - ;; ``` fennel - ;; (let [_1_ (foo) - ;; (x y) _1_] - ;; ((fn recur [(x y)] ...) _1_) - ;; ``` - ;; - ;; `x` is correctly set, but `y` is completely lost. Therefore, this - ;; macro checks for lists in bindings. - ;; [sym1# sym2# etc...], for the function application below (table.insert gensyms gs) |