unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Re: Better documentation for non-binding clauses of if-let and friends
@ 2024-11-11  9:28 arthur miller
  2024-11-11  9:58 ` Alfred M. Szmidt
  0 siblings, 1 reply; 10+ messages in thread
From: arthur miller @ 2024-11-11  9:28 UTC (permalink / raw)
  To: Alfred M. Szmidt, emacs-devel@gnu.org

[-- Attachment #1: Type: text/plain, Size: 1368 bytes --]

>   I rather agree with Joost on that other thread regarding the
>   usefulness of the FOO-let macros and their condition-only,
>   non-binding clauses.
>
>I don't think anyone is arguing about their usefulness, only that the
>macros are too smart for their own good.

I think I will start argue against while-let usefulness :).

Now when I have seen that while-let is a special case of named-let,
I think that while-let is a bad construct for some reasons:

1. it is not general
2. the special case in which it does not work is hidden
3. the semantic of "read-only" loop variables is uncommon and unexpected

>   > There is no mention of this in the manual, that only says that SPEC is
>   > like the one in LET*.

That is the gotcha that got me: it says SPEC is "like let*", so this "-let*"
in the name take my mind to believe it established ordinary let*-bindings.
However, in while-let, these are not ordinary, but read-only. So they are
not the same, since they don't obey the ordinary behavior of let* bindings.

>   But I agree with you that the manual is incomplete or even
>   wrong here.

If that semantic of while-let is desirable to have, than the manual would
have to catch the details of while-let and its non-general nature, read-only
semantic of bindings and perhaps mention the named-let as a more general
 alternative.

[-- Attachment #2: Type: text/html, Size: 5172 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Better documentation for non-binding clauses of if-let and friends
  2024-11-11  9:28 Better documentation for non-binding clauses of if-let and friends arthur miller
@ 2024-11-11  9:58 ` Alfred M. Szmidt
  2024-11-11 10:23   ` Sv: " arthur miller
  2024-11-11 10:26   ` Joost Kremers
  0 siblings, 2 replies; 10+ messages in thread
From: Alfred M. Szmidt @ 2024-11-11  9:58 UTC (permalink / raw)
  To: arthur miller; +Cc: emacs-devel

   That is the gotcha that got me: it says SPEC is "like let*", so this "-let*"
   in the name take my mind to believe it established ordinary let*-bindings.
   However, in while-let, these are not ordinary, but read-only. So they are
   not the same, since they don't obey the ordinary behavior of let* bindings.

I agree, but the question really is what should be done -- either
satisfy one camp or the other.

I personally do not get what one gets from using WHILE-LET -- the
construct seems forced, and very rare to use.

IF-LET, WHEN-LET I can maybe guess but they too seem force constructs,
and why isn't there a UNLESS-LET and OR-LET*? But I'll leave it at
that, personal preferences and all.

   >   But I agree with you that the manual is incomplete or even
   >   wrong here.

   If that semantic of while-let is desirable to have, than the manual would
   have to catch the details of while-let and its non-general nature, read-only
   semantic of bindings and perhaps mention the named-let as a more general
    alternative.

Yup.



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Sv: Better documentation for non-binding clauses of if-let and friends
  2024-11-11  9:58 ` Alfred M. Szmidt
@ 2024-11-11 10:23   ` arthur miller
  2024-11-11 10:26   ` Joost Kremers
  1 sibling, 0 replies; 10+ messages in thread
From: arthur miller @ 2024-11-11 10:23 UTC (permalink / raw)
  To: Alfred M. Szmidt; +Cc: emacs-devel@gnu.org

[-- Attachment #1: Type: text/plain, Size: 932 bytes --]

>   That is the gotcha that got me: it says SPEC is "like let*", so this "-let*"
>   in the name take my mind to believe it established ordinary let*-bindings.
>   However, in while-let, these are not ordinary, but read-only. So they are
>   not the same, since they don't obey the ordinary behavior of let* bindings.
>
>I agree, but the question really is what should be done -- either
>satisfy one camp or the other.

My guess is while-let can't go away since it is used in many places in Emacs
already. But the docs and the manual can be updated and clarified.

>I personally do not get what one gets from using WHILE-LET -- the
>construct seems forced, and very rare to use.

With the facit in the hand, I agree with you. In practice it saves some typing
in well-chosen contexts. But question is if it is a good practice to have a
construct that seemingly looks like a general let-construct, but really is not.

[-- Attachment #2: Type: text/html, Size: 3145 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Better documentation for non-binding clauses of if-let and friends
  2024-11-11  9:58 ` Alfred M. Szmidt
  2024-11-11 10:23   ` Sv: " arthur miller
@ 2024-11-11 10:26   ` Joost Kremers
  2024-11-11 10:53     ` Sv: " arthur miller
  2024-11-11 21:21     ` [External] : " Drew Adams
  1 sibling, 2 replies; 10+ messages in thread
From: Joost Kremers @ 2024-11-11 10:26 UTC (permalink / raw)
  To: Alfred M. Szmidt; +Cc: arthur miller, emacs-devel

On Mon, Nov 11 2024, Alfred M. Szmidt wrote:
> IF-LET, WHEN-LET I can maybe guess but they too seem force constructs,

I just recently wrote a simple RDP that has a couple of functions of this
form:

```
(if-let* (((parsebib--char "@"))
          ((parsebib--keyword '("string")))
          (open (parsebib--char "{("))
          (definition (parsebib--assignment))
          ((parsebib--char (alist-get open '((?\{ . "}") (?\( . ")"))))))
    definition
  (signal 'parsebib-error (list (format "Malformed @String definition at position %d,%d"
                                        (line-number-at-pos) (current-column)))))
```

This reads a complete BibTeX @String definition. The clauses in the
`if-let*` specify what text should be read, and I can capture the result of
those reads, but only for the parts that I need to capture.

I think once you know how `if-let*` works, this is a fairly concise and
quite readable way to code this rule. Not sure what the equivalent without
`if-let*` would look like, TBH. (Short of creating a DSL, which would
admittedly be nicer...)


-- 
Joost Kremers
Life has its moments



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Sv: Better documentation for non-binding clauses of if-let and friends
  2024-11-11 10:26   ` Joost Kremers
@ 2024-11-11 10:53     ` arthur miller
  2024-11-11 11:18       ` Joost Kremers
  2024-11-11 21:21     ` [External] : " Drew Adams
  1 sibling, 1 reply; 10+ messages in thread
From: arthur miller @ 2024-11-11 10:53 UTC (permalink / raw)
  To: Joost Kremers, Alfred M. Szmidt; +Cc: emacs-devel@gnu.org

[-- Attachment #1: Type: text/plain, Size: 1397 bytes --]

>I just recently wrote a simple RDP that has a couple of functions of this
>form:
>
>```
>(if-let* (((parsebib--char "@"))
>          ((parsebib--keyword '("string")))
>          (open (parsebib--char "{("))
>          (definition (parsebib--assignment))
>          ((parsebib--char (alist-get open '((?\{ . "}") (?\( . ")"))))))
>    definition
>  (signal 'parsebib-error (list (format "Malformed @String definition at position %d,%d"
>                                        (line-number-at-pos) (current-column)))))
>```
>
>This reads a complete BibTeX @String definition. The clauses in the
>`if-let*` specify what text should be read, and I can capture the result of
>those reads, but only for the parts that I need to capture.
>
>I think once you know how `if-let*` works, this is a fairly concise and
>quite readable way to code this rule. Not sure what the equivalent without
>`if-let*` would look like, TBH. (Short of creating a DSL, which would
>admittedly be nicer...)

In my opinion, if-let and when-let are no brainer. they are actualy useful as a
synctatic-sugar and help keep variables in the scope of ift- and when-let. Those
bindings are usable as ordinary let-bindings, since they are established only once.

Problem is when one introduce repetition: while-let re-establishes the bindings a new
for each repetition, which is a bit unusual I believe.

[-- Attachment #2: Type: text/html, Size: 5037 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Sv: Better documentation for non-binding clauses of if-let and friends
  2024-11-11 10:53     ` Sv: " arthur miller
@ 2024-11-11 11:18       ` Joost Kremers
  0 siblings, 0 replies; 10+ messages in thread
From: Joost Kremers @ 2024-11-11 11:18 UTC (permalink / raw)
  To: arthur miller; +Cc: Alfred M. Szmidt, emacs-devel@gnu.org

On Mon, Nov 11 2024, arthur miller wrote:
> Problem is when one introduce repetition: while-let re-establishes the bindings a new
> for each repetition, which is a bit unusual I believe.

I agree it's not immediately obvious, especially if you're trying to grok
`while-let` on the basis of `while`, so it should definitely be pointed out
in the documentation in the manual and the doc string, which currently
isn't the case.

It might be more obvious if you take `let` as your starting point for
grokking `while-let`, but then the non-binding forms can be confusing. So
that should be mentioned more explicitly as well, I think.

I do think reestablishing the bindings on each iteration makes sense, given
the purpose of `while-let`. I don't think it would be a very useful macro
if it were otherwise. But I have to admit I haven't yet had any occasion to
use `while-let`, and I haven't studied it in the wild, so I can't be
entirely sure. (Though I imagine that inside the loop, you would want to
use the *current* value of each of the conditions, not the value
established on the first iteration.)

-- 
Joost Kremers
Life has its moments



^ permalink raw reply	[flat|nested] 10+ messages in thread

* RE: [External] : Re: Better documentation for non-binding clauses of if-let and friends
  2024-11-11 10:26   ` Joost Kremers
  2024-11-11 10:53     ` Sv: " arthur miller
@ 2024-11-11 21:21     ` Drew Adams
  2024-11-11 22:51       ` Joost Kremers
  1 sibling, 1 reply; 10+ messages in thread
From: Drew Adams @ 2024-11-11 21:21 UTC (permalink / raw)
  To: Joost Kremers, Alfred M. Szmidt; +Cc: arthur miller, emacs-devel@gnu.org

> (if-let* (((parsebib--char "@"))
>           ((parsebib--keyword '("string")))
>           (open (parsebib--char "{("))
>           (definition (parsebib--assignment))
>           ((parsebib--char (alist-get open '((?\{ . "}") (?\( . ")"))))))
>     definition
>   (signal 'parsebib-error
>           (list (format "Malformed @String definition at position %d,%d"
>                         (line-number-at-pos) (current-column)))))
...

> Not sure what the equivalent without `if-let*` would look like, TBH.

Just `macroexpand' it, to see one equivalent, at least:

(let* ((s           (and t (parsebib--char "@"))) 
       (s           (and s (parsebib--keyword '("string"))))
       (open        (and s (parsebib--char "{(")))
       (definition  (and open (parsebib--assignment)))
       (s (and definition (parsebib--char
                            (alist-get open '((123 . "}") (40 . ")")))))))
  (if s 
      definition
    (signal 'parsebib-error
            (list (format "Malformed @String definition at position %d,%d" 
					 (line-number-at-pos) (current-column))))))

Or just this, if hand-coding (but you'd use a better name than `s'):

(let*((open        (and (parsebib--char "@")
                        (parsebib--keyword '("string"))
                        (parsebib--char "{(")))
      (definition  (and open (parsebib--assignment)))
      (s           (and definition
                        (parsebib--char 
                          (alist-get open '((123 . "}") (40 . ")")))))))
  (if s 
      definition
    (signal 'parsebib-error
            (list (format "Malformed @String definition at position %d,%d" 
					 (line-number-at-pos) (current-column))))))

Did you really gain anything?  Debatable.

If you replace newline and multiple space runs with
just a single space then you can compare the if-let*
with the let* sexp above:

if-let*: 335 chars
let*   : 369 chars (+ a few, with name other than `s')

Beauty and clarity are in the eye of the beholder, of
course.  YMMV.

But at least the plain let* doesn't require or invite
a long discussion about what's really going on, as in
this thread and its siblings.



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [External] : Re: Better documentation for non-binding clauses of if-let and friends
  2024-11-11 21:21     ` [External] : " Drew Adams
@ 2024-11-11 22:51       ` Joost Kremers
  2024-11-12  0:26         ` Drew Adams
  0 siblings, 1 reply; 10+ messages in thread
From: Joost Kremers @ 2024-11-11 22:51 UTC (permalink / raw)
  To: Drew Adams; +Cc: Alfred M. Szmidt, arthur miller, emacs-devel@gnu.org

On Mon, Nov 11 2024, Drew Adams wrote:
> (let*((open        (and (parsebib--char "@")
>                         (parsebib--keyword '("string"))
>                         (parsebib--char "{(")))
>       (definition  (and open (parsebib--assignment)))
>       (s           (and definition
>                         (parsebib--char 
>                           (alist-get open '((123 . "}") (40 . ")")))))))
>   (if s 
>       definition
>     (signal 'parsebib-error
>             (list (format "Malformed @String definition at position %d,%d" 
> 					 (line-number-at-pos) (current-column))))))
[...]
> Beauty and clarity are in the eye of the beholder, of
> course.  YMMV.

Yeah, I'm afraid I prefer the `if-let*` version. IMHO it more closely
reflects the parser grammar rule; it avoids unnecessary repetition of
variables (`open` and `definition` here) and the `and` forms that I feel
just obscure what's actually relevant.

-- 
Joost Kremers
Life has its moments



^ permalink raw reply	[flat|nested] 10+ messages in thread

* RE: [External] : Re: Better documentation for non-binding clauses of if-let and friends
  2024-11-11 22:51       ` Joost Kremers
@ 2024-11-12  0:26         ` Drew Adams
  2024-11-12  8:07           ` Joost Kremers
  0 siblings, 1 reply; 10+ messages in thread
From: Drew Adams @ 2024-11-12  0:26 UTC (permalink / raw)
  To: Joost Kremers; +Cc: Alfred M. Szmidt, arthur miller, emacs-devel@gnu.org

> > (let*((open        (and (parsebib--char "@")
           ^^^^1
> >                         (parsebib--keyword '("string"))
> >                         (parsebib--char "{(")))
> >       (definition  (and open (parsebib--assignment)))
           ^^^^^^^^^^1      ^^^^2
> >       (s           (and definition
                            ^^^^^^^^^^2
> >                         (parsebib--char
> >                           (alist-get
> >                             open '((123 . "}") (40 . ")")))))))
                                ^^^^3
> >   (if s
> >       definition
          ^^^^^^^^^^3
> >     (signal 'parsebib-error
> >             (list (format "Malformed @String definition at position %d,%d"
> > 					 (line-number-at-pos) (current-column))))))
> [...]
>
> > Beauty and clarity are in the eye of the beholder, of
> > course.  YMMV.
> 
> Yeah, I'm afraid I prefer the `if-let*` version.

NP. Les goûts et les couleurs.

> IMHO it more closely
> reflects the parser grammar rule; it avoids unnecessary repetition of
> variables (`open` and `definition` here) and the `and` forms that I feel
> just obscure what's actually relevant.

It avoids one repetition each of vars `open' and
`definition'.  Those occurrences make explicit
the `and' dependencies implicit in `if-let*
(necessary/relevant dependencies).

This is your if-let*, with space separation of
bindings from conditions:

> (if-let* ((           (parsebib--char "@"))
>           (           (parsebib--keyword '("string")))
>           (open       (parsebib--char "{("))
             ^^^^1
>           (definition (parsebib--assignment))
             ^^^^^^^^^^1
>           (           (parsebib--char
                          (alist-get
                            open '((?\{ . "}") (?\( . ")"))))))
                            ^^^^2
>     definition
      ^^^^^^^^^^2
>   (signal 'parsebib-error
>           (list (format "Malformed @String definition at position %d,%d"
>                         (line-number-at-pos) (current-column)))))

Implied and-ing is all the if-let* gets you, at
least in this example.  At the cost of a loss of
obvious distinction between binding and use
occurrences.

I guess your parser grammar rule (not shown) just
has the (parsebib-*...) conditions as literal
match patterns, and it has `open' and `definition'
as nonterminals.  (Or maybe `definition' is a
terminal, since you return that.)

In any case, you said you didn't know what an
"equivalent without `if-let*` would look like".
So I mentioned that `macroexpand' has the answer.
And the answer isn't very complex.  In this case,
at least, it just makes the and-ing of binding
dependencies explicit/clear.



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [External] : Re: Better documentation for non-binding clauses of if-let and friends
  2024-11-12  0:26         ` Drew Adams
@ 2024-11-12  8:07           ` Joost Kremers
  0 siblings, 0 replies; 10+ messages in thread
From: Joost Kremers @ 2024-11-12  8:07 UTC (permalink / raw)
  To: Drew Adams; +Cc: Alfred M. Szmidt, arthur miller, emacs-devel@gnu.org

On Tue, Nov 12 2024, Drew Adams wrote:
> This is your if-let*, with space separation of
> bindings from conditions:
>
>> (if-let* ((           (parsebib--char "@"))
>>           (           (parsebib--keyword '("string")))
>>           (open       (parsebib--char "{("))
>              ^^^^1
>>           (definition (parsebib--assignment))
>              ^^^^^^^^^^1
>>           (           (parsebib--char
>                           (alist-get
>                             open '((?\{ . "}") (?\( . ")"))))))
>                             ^^^^2
>>     definition
>       ^^^^^^^^^^2
>>   (signal 'parsebib-error
>>           (list (format "Malformed @String definition at position %d,%d"
>>                         (line-number-at-pos) (current-column)))))
>
> Implied and-ing is all the if-let* gets you, at
> least in this example.  At the cost of a loss of
> obvious distinction between binding and use
> occurrences.

I don't see the loss. The `if-let*` version has two occurrences of `open`
and `definition`: the first is the binding, the second is the use. Without
`if-let*`, I need to repeat both once more to test if they're non-nil.
Those occurrences and the `and`s are there to make the code do what I want,
but they don't help express the idea behind the code, so to my mind they're
boilerplate.

> I guess your parser grammar rule (not shown) just
> has the (parsebib-*...) conditions as literal
> match patterns, and it has `open' and `definition'
> as nonterminals.

`post` is also a literal, I'm just capturing it so I can make sure I get
the corresponding closing delimiter. Otherwise you could close a curly
brace { with a parenthesis ) or vice versa. (Yes, this is cheating, I
know...) `definition` is a non-terminal (and should probably be called
`assignment`).

> In any case, you said you didn't know what an
> "equivalent without `if-let*` would look like".
> So I mentioned that `macroexpand' has the answer.

True, it didn't occur to me to look at it.

> And the answer isn't very complex.  In this case,
> at least, it just makes the and-ing of binding
> dependencies explicit/clear.

Which I feel is unnecessary and even a hindrance.

-- 
Joost Kremers
Life has its moments



^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2024-11-12  8:07 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-11-11  9:28 Better documentation for non-binding clauses of if-let and friends arthur miller
2024-11-11  9:58 ` Alfred M. Szmidt
2024-11-11 10:23   ` Sv: " arthur miller
2024-11-11 10:26   ` Joost Kremers
2024-11-11 10:53     ` Sv: " arthur miller
2024-11-11 11:18       ` Joost Kremers
2024-11-11 21:21     ` [External] : " Drew Adams
2024-11-11 22:51       ` Joost Kremers
2024-11-12  0:26         ` Drew Adams
2024-11-12  8:07           ` Joost Kremers

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).