Live data from Hacker News

Fun with Macros: If-Let and When-Let

stevelosh.com

21–30 of 31 posts

Re: Fun with Macros: If-Let and When-Let

#21

Earlier quoted context omitted.

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...

> We stole it from Swift. 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).

I believe thats right, yeah.

And sure, you could make that argument, but in terms of how it happened, it was largely “oh, that looks good. We should do that.” So maybe we modified it a bit, but still. :)

Re: Fun with Macros: If-Let and When-Let

#22
post #2

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.

> What’s a example of these macros being used usefully? Sure, here are a couple examples I found from grepping my src directory. roguelikelike game I made for a game jam: https://github.com/sjl/vintage/blob/master/src/main.lisp#L19... prolog compiler: https://github.com/sjl/temperance/blob/master/src/compiler/3... cl port of robotfindskitten: https://github.com/sjl/sattyrday/blob/master/src/002-afk/mai... generative…

Thanks.

> I'm not sure what "new names for variables" means here.

All the examples I could find were of the template

    (when-let ((name1 scalar1)
               (name2 scalar2)
        ...)
where presumably the `scalar`s were standins for existing named bindings. Same for `if-let`.

I wasn't awake enough to contemplate that the `scalar`s above could probably be arbitrary expressions.

But skimming your blog post again, I do see this example about half-way through:

    (let* ((name (read-string))
           (length (length name)))
      ; ...
      )

Re: Fun with Macros: If-Let and When-Let

#23
post #6
post #3

Earlier quoted context omitted.

They're pure convenience, but they're pretty convenient. It's very common to have patterns like: (let ((result (do-some-calculation))) (when result (operate-on result))) `when-let' would just skip one "layer" of code, and I believe avoid assigning a local variable if (do-some-calculation) fails. Just a convenience. (edit: trying to fix formatting)

Never mind, it still binds a local variable (at least in elisp, where I just tried it).

It has to, otherwise it would have to execute the initform twice.

Re: Fun with Macros: If-Let and When-Let

#24

Earlier quoted context omitted.

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...

> We stole it from Swift. 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).

nothing wrong with stealing! great artists and all that. :-)

Re: Fun with Macros: If-Let and When-Let

#26
post #2

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.

Oh, it comes up all the time when doing pattern-matching, which many Lisp programs do a lot of. "Anaphoric" macros like AIF [0] are one solution:

  (awhen (foo 3) (bar it))
Here IT is bound to the result of (FOO 3), provided that's nonnull. I don't really care for this style, but I see a lot of people using it. I have a macro I sometimes use for this purpose called BCOND ("binding COND"), e.g.:

  (bcond ((let ((x (foo 3)))
            x)
          (bar x)))
The rule is that if the test-form of a BCOND clause is a LET form, the scope of the bindings is extended to the end of the clause. I'm not going to say it's beautiful, but it's quite general; in particular, an arbitrary predicate can be applied, not only a nonnull test, and can involve any or all of the bound variables. I wrote the first version of BCOND in 1980, so I think it's safe to say the need it attempts to address is felt with some frequency.

[0] https://www.common-lisp.net/project/anaphora/

Re: Fun with Macros: If-Let and When-Let

#27
Maybe I'm missing the point of the exercise, but since the author is already using Alexandria in these examples, and Alexandria provides when-let and if-let macros as well, then... why not just explain - or at least compare with and discuss - the implementation in Alexandria?

Skipping docstrings, those are exactly:

  (defmacro if-let (bindings &body (then-form &optional else-form))
      (let* ((binding-list (if (and (consp bindings) (symbolp (car bindings)))
                               (list bindings)
                               bindings))
           (variables (mapcar #'car binding-list)))
      `(let ,binding-list
         (if (and ,@variables)
             ,then-form
             ,else-form))))
  
  (defmacro when-let (bindings &body forms)
    (let* ((binding-list (if (and (consp bindings) (symbolp (car bindings)))
                             (list bindings)
                             bindings))
           (variables (mapcar #'car binding-list)))
      `(let ,binding-list
         (when (and ,@variables)
           ,@forms))))

Re: Fun with Macros: If-Let and When-Let

#28

Maybe I'm missing the point of the exercise, but since the author is already using Alexandria in these examples, and Alexandria provides when-let and if-let macros as well , then... why not just explain - or at least compare with and discuss - the implementation in Alexandria? Skipping docstrings, those are exactly: (defmacro if-let (bindings &body (then-form &optional else-form)) (let* ((binding-list (if (and (consp…

Would you rob the author the fun of reinvention and rediscovery?

Re: Fun with Macros: If-Let and When-Let

#29

Maybe I'm missing the point of the exercise, but since the author is already using Alexandria in these examples, and Alexandria provides when-let and if-let macros as well , then... why not just explain - or at least compare with and discuss - the implementation in Alexandria? Skipping docstrings, those are exactly: (defmacro if-let (bindings &body (then-form &optional else-form)) (let* ((binding-list (if (and (consp…

Alexandria's versions are basically the same as the "multiple bindings" versions, about halfway through the post[1]. So they are explained/compared/discussed... I just didn't mention that those versions happen to be in Alexandria. I suppose I could add a note, though calling out a particular library for having a less than ideal implementation seems a little rude.

[1]: They do allow a single binding as a special case, true.

Re: Fun with Macros: If-Let and When-Let

#30

Maybe I'm missing the point of the exercise, but since the author is already using Alexandria in these examples, and Alexandria provides when-let and if-let macros as well , then... why not just explain - or at least compare with and discuss - the implementation in Alexandria? Skipping docstrings, those are exactly: (defmacro if-let (bindings &body (then-form &optional else-form)) (let* ((binding-list (if (and (consp…

Alexandria's versions are basically the same as the "multiple bindings" versions, about halfway through the post[1]. So they are explained/compared/discussed... I just didn't mention that those versions happen to be in Alexandria. I suppose I could add a note, though calling out a particular library for having a less than ideal implementation seems a little rude. [1]: They do allow a single binding as a special case,…

> calling out a particular library for having a less than ideal implementation seems a little rude

Why? As long as you call it "less than ideal", and not say "it sucks" :). Maybe in the end a patch will find its way upstream; I hear that someone is contributing to this library, sometimes.

Post reply on HN