mirror of
https://github.com/janet-lang/janet
synced 2026-10-14 03:31:40 +00:00
Extend the scope of truthy if-let bindings to the fal branch
The `if-let` construct - together with the fact that only `nil` and `false` are falsey - is very handy to express monadic chains over maybe/optional values:
```janet
(if-let [resp (make-http-request) # is nil on failure
{:status stat :body body} resp # always truthy, binds `stat` and `body`
check (= 200 stat) # continue only on 200 OK
json (json/decode body true) # is nil on failure
{:foo foo} json]
foo)
```
However, none of the bindings are available in the false branch, which
could be helpful to have for error reporting or fallback logic:
```janet
(if-let [resp (make-http-request)
{:status stat :body body} resp
check (= 200 stat)
json (json/decode body true)
{:foo foo} json]
foo
(if (= 401 stat) # unauthorized, refresh the token
(try-again)
nil))
```
This PR makes all symbols introduced by the bindings of an `if-let` available in all branches.
This commit is contained in:
+25
-9
@@ -450,22 +450,38 @@
|
||||
evaluate the `tru` form. If any are false or nil, evaluate
|
||||
the `fal` form. Bindings have the same syntax as the `let` macro.``
|
||||
[bindings tru &opt fal]
|
||||
(def len (length bindings))
|
||||
(def len (/ (length bindings) 2))
|
||||
(if (= 0 len) (error "expected at least 1 binding"))
|
||||
(if (odd? len) (error "expected an even number of bindings"))
|
||||
(if-not (int? len) (error "expected an even number of bindings"))
|
||||
(def fal2 (if macexvar (macexvar fal) fal))
|
||||
(def syms (array/new len))
|
||||
(defn fill-syms [i]
|
||||
(when (< i len)
|
||||
(let [bl (in bindings (* i 2))
|
||||
sym (if (symbol? bl) bl (gensym))]
|
||||
(put syms i sym))
|
||||
(fill-syms (+ 1 i))))
|
||||
(defn aux [i]
|
||||
(if (>= i len)
|
||||
tru
|
||||
(do
|
||||
(def bl (in bindings i))
|
||||
(def br (in bindings (+ 1 i)))
|
||||
(def bl (in bindings (* 2 i)))
|
||||
(def br (in bindings (+ 1 (* 2 i))))
|
||||
(if (symbol? bl)
|
||||
~(if (def ,bl ,br) ,(aux (+ 2 i)) ,fal2)
|
||||
~(if (def ,(def sym (gensym)) ,br)
|
||||
(do (def ,bl ,sym) ,(aux (+ 2 i)))
|
||||
,fal2)))))
|
||||
(aux 0))
|
||||
~(if (def ,bl ,br) ,(aux (+ 1 i)) ,fal2)
|
||||
(do
|
||||
(def sym (in syms i))
|
||||
~(do (def ,sym ,br) (if (def ,bl ,sym) ,(aux (+ 1 i)) ,fal2)))))))
|
||||
|
||||
(defn wrap [i]
|
||||
(if (>= i len)
|
||||
(aux 0)
|
||||
~(do
|
||||
(def ,(in syms i) ,nil)
|
||||
,(wrap (+ 1 i)))))
|
||||
|
||||
(fill-syms 0)
|
||||
(wrap 0))
|
||||
|
||||
(defmacro when-with
|
||||
``Similar to with, but if binding is false or nil, returns
|
||||
|
||||
@@ -141,6 +141,10 @@
|
||||
(assert (= (if-let [[a b] my-array] a) 1) "if-let 5")
|
||||
(assert (= (if-let [{:a a :b b} {:a 1 :b 2}] b) 2) "if-let 6")
|
||||
(assert (= (if-let [[a b] nil] :t :f) :f) "if-let 7")
|
||||
(assert (= (if-let [[a b] ["a" "b"]] a a) "a"))
|
||||
(assert (= (if-let [[a b] ["a" "b"]] b b) "b"))
|
||||
(assert (= (if-let [[a b] ["a" nil]] a a) "a"))
|
||||
(assert (= (if-let [[a b] ["a" nil]] b b) nil))
|
||||
|
||||
# #1191
|
||||
(var cnt 0)
|
||||
|
||||
Reference in New Issue
Block a user