summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrey Listopadov <andreyorst@gmail.com>2022-10-31 14:24:33 +0300
committerAndrey Listopadov <andreyorst@gmail.com>2022-10-31 14:24:33 +0300
commitd5c73f5ee4a5b769c691b56adf80bb1c3c10f5ee (patch)
tree761f8b31fbcc0effb46181e5832a3d05c1e5f6aa
parent44fa0c9514acade4b7dc80be1b1a1dcd5b3b0326 (diff)
update loop doc
-rw-r--r--doc/core.md8
-rw-r--r--doc/macros.md54
-rw-r--r--init-macros.fnl98
-rw-r--r--init.fnl8
4 files changed, 107 insertions, 61 deletions
diff --git a/doc/core.md b/doc/core.md
index 74bd2c3..014be20 100644
--- a/doc/core.md
+++ b/doc/core.md
@@ -1474,13 +1474,13 @@ Returns a new coll consisting of `to` with all of the items of
Thransofmr a hash-map into a sequence of key-value pairs:
-```fennel
+``` fennel
(assert-eq [[:a 1]] (into (vector) {:a 1}))
```
Construct a hash-map from a sequence of key-value pairs:
-```fennel
+``` fennel
(assert-eq {:a 1 :b 2 :c 3}
(into (hash-map) [[:a 1] [:b 2] [:c 3]]))
```
@@ -1548,7 +1548,7 @@ iterator style with the `doseq` macro.
Bear in mind, that since the sequence is lazy it should be realized or
truncated before the file is closed:
-```fennel
+``` fennel
(let [lines (with-open [f (io.open "init.fnl" :r)]
(line-seq f))]
;; this will error because only first line was realized, but the
@@ -1622,7 +1622,7 @@ no collection is provided.
### Examples
-```fennel
+``` fennel
(map #(+ $ 1) [1 2 3]) ;; => @seq(2 3 4)
(map #(+ $1 $2) [1 2 3] [4 5 6]) ;; => @seq(5 7 9)
(def :private res (map #(+ $ 1) [:a :b :c])) ;; will raise an error only when realized
diff --git a/doc/macros.md b/doc/macros.md
index 8021db9..9c20e56 100644
--- a/doc/macros.md
+++ b/doc/macros.md
@@ -258,7 +258,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)
@@ -279,7 +279,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
>> (ns foo)
nil
>> (def x 42)
@@ -342,7 +342,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)
@@ -361,6 +361,50 @@ 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)
+### Limitations
+
+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.
+
## `ns`
Function signature:
@@ -378,7 +422,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
(ns some-namespace
"Description of the some-namespace."
(:require [some.lib]
@@ -390,7 +434,7 @@ without a prefix:
Which is equivalent to:
-```
+``` fennel
(local some-namespace {})
(local lib (require :some.lib))
(local lib2 (require :some.other.lib))
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)
diff --git a/init.fnl b/init.fnl
index 67bbde7..bf56859 100644
--- a/init.fnl
+++ b/init.fnl
@@ -786,7 +786,7 @@ no collection is provided.
# Examples
-```fennel
+``` fennel
(map #(+ $ 1) [1 2 3]) ;; => @seq(2 3 4)
(map #(+ $1 $2) [1 2 3] [4 5 6]) ;; => @seq(5 7 9)
(def :private res (map #(+ $ 1) [:a :b :c])) ;; will raise an error only when realized
@@ -1245,7 +1245,7 @@ iterator style with the `doseq` macro.
Bear in mind, that since the sequence is lazy it should be realized or
truncated before the file is closed:
-```fennel
+``` fennel
(let [lines (with-open [f (io.open \"init.fnl\" :r)]
(line-seq f))]
;; this will error because only first line was realized, but the
@@ -2057,13 +2057,13 @@ raise an error."
Thransofmr a hash-map into a sequence of key-value pairs:
-```fennel
+``` fennel
(assert-eq [[:a 1]] (into (vector) {:a 1}))
```
Construct a hash-map from a sequence of key-value pairs:
-```fennel
+``` fennel
(assert-eq {:a 1 :b 2 :c 3}
(into (hash-map) [[:a 1] [:b 2] [:c 3]]))
```"