Paul Graham called these "anaphoric" macros in On LISP.
If you want actual anaphoric macros in CL, here they are: https://www.common-lisp.net/project/anaphora/
11–20 of 31 posts
Paul Graham called these "anaphoric" macros in On LISP.
If you want actual anaphoric macros in CL, here they are: https://www.common-lisp.net/project/anaphora/
What’s a example of these macros being used usefully? All I can find are contrived examples. Why not use `if` and `when` directly? Is creating new names for variables — which is what `if-let` and `when-let` seems to contribute — that common of a pattern? I must be missing something.
http://www.kylheku.com/cgit/txr/tree/share/txr/stdlib/compil...
Note that they all support multiple bindings, but not the and semantics: only the last binding is tested. Treating a nil value out of any of the bindings as a failure is way too constraining.
It is also permissible to omit the variable name from the last binding; in that case the expression is used as the controlling expression; e.g.
(whenlet ((a (expr1))
(b (expr2))) ;; we can delete b, if not needed
...)
Therefore, this is possible: (whenlet ((x (expr1))
(y (expr2))
...
((complex-expr x y ...)))
...)
That is, bind some variables (that may or may not be nil: they are not being tested), and then reference them in a complex test expression.See the me_iflet_whenlet function in eval.c:
(defmacro when-let (bindings &rest body)
`(if-let ,bindings
(progn ,body)))
That's how it's implemented in Emacs at least. Maybe there are some edge cases involving declarations or whatever where this wouldn't work.You could save a little work by noting that when is a special case of if and then writing when-let as a special case of if-let . Something like: (defmacro when-let (bindings &rest body) `(if-let ,bindings (progn ,body))) That's how it's implemented in Emacs at least. Maybe there are some edge cases involving declarations or whatever where this wouldn't work.
Earlier quoted context omitted.
I find them pretty useful in Clojure. Note that it’s practically never about assigning a strictly boolean-valued expression (which would indeed be pretty useless) but rather something that is ”truthy” or ”falsy” ie. implicitly convertible to boolean and you want to do something with the actual value if it is truthy. Other languages have similar constructs, eg. C++: if(auto p = get_ptr()) ... And Rust: if let Some(val…
We stole it from Swift. To your parent, the RFC is pretty short and has good motivation: https://github.com/rust-lang/rfcs/blob/master/text/0160-if-l...
Stole is probably too harsh a statement given the semantics are pretty different: Swift's if-let has closer semantics to TFA's as it only works with Optional but allows multiple patterns (bindings in TFA's version) while Rust's works with any pattern but only allows a single pattern (AFAIK).
What’s a example of these macros being used usefully? All I can find are contrived examples. Why not use `if` and `when` directly? Is creating new names for variables — which is what `if-let` and `when-let` seems to contribute — that common of a pattern? I must be missing something.
The point of homoiconic languages like lisps, where the language is just the syntax tree, is that you can make your own nodes. `if-let` and `when-let` are patterns, they don't have to be common, the definition of which remove at least one node. It's not any different than the idea of having lots of small functions but at the syntax layer. Do this enough time and your actual business logic starts get pretty terse.
(define-syntax if-let
(syntax-rules ()
((if-let ((var value) ...)
consequent ...)
(let ((var value) ...)
(if (and var ...)
consequent ...)))))
(define-syntax when-let
(syntax-rules ()
((when-let (binding)
body ...)
(if-let (binding)
(begin body ...)))))You could save a little work by noting that when is a special case of if and then writing when-let as a special case of if-let . Something like: (defmacro when-let (bindings &rest body) `(if-let ,bindings (progn ,body))) That's how it's implemented in Emacs at least. Maybe there are some edge cases involving declarations or whatever where this wouldn't work.
You'd need to parse out the declarations, yeah. Otherwise they'd get shoved inside the progn which wouldn't work.
I like the macro transformers in R5RS Scheme. Here's how the multi-binding if-let and when-let look: (define-syntax if-let (syntax-rules () ((if-let ((var value) ...) consequent ...) (let ((var value) ...) (if (and var ...) consequent ...))))) (define-syntax when-let (syntax-rules () ((when-let (binding) body ...) (if-let (binding) (begin body ...)))))
(mac iflet (var condition . body)
`(let ,var ,condition
(if ,var ,@body)))
(mac whenlet (var condition . body)
`(iflet ,var ,condition (do ,@body)))