unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* cond* Examples
@ 2025-01-06 14:55 Psionic K
  2025-01-09  3:51 ` Richard Stallman
  0 siblings, 1 reply; 3+ messages in thread
From: Psionic K @ 2025-01-06 14:55 UTC (permalink / raw)
  To: Emacs developers

I'm putting together some examples on Elisp forms and wanted to get
ahead regarding use cases where cond* is more ergonomic.

The closest example I can relate from other languages is a Rust match
statement, which both destructures and binds before taking one
matching arm.  Since cond-star's package docs say it's a replacement
for `pcase', that explanation is consistent at a surface level.

Regarding `bind*':
> it counts as true if the first binding's value is non-nil.

This feels inconsistent with `when-let*' and `if-let*' behavior, where
there's an intuitive reason that every bind must succeed for the first
or all body forms to evaluate.  Is this a first-pass tradeoff?

Regarding exit clauses
> and it exits the `cond*'
From this, I expected the next part:

> If a clause has only one element, or if its first element is
> t, or if it ends with the keyword :non-exit, then
> this clause never exits the `cond*' construct.  Instead,
> control falls through to the next clause (if any).
> The bindings made in CONDITION for the BODY of the non-exit clause
> re passed along to the rest of the clauses in this `cond*' construct.

Yeah, okay, so we're definitely evaluating multiple BODYs in some cases.

I read the tests, new manual section, and news.  I couldn't find the
mailing list entries since I didn't subscribe and while I'm positive
I've seen many cond* threads, no query that I provided Namazu was
effective.

In any case, the goal is to identify 1) the point where cond* becomes
necessary.  For example, in the case of let*, I can say that whenever
bindings become dependent, it is necessary to use let*.  2) where
cond* becomes advantageous.  For example, when using let* over its
equivalent sequentially nested let form, the advantage is readily
evident in the reduction of form repetition.  It's natural that
necessity and advantage tend to go together here.

From what I can gather and deduce, cond* is a variation of a cond
where I want to compose several destructurings into the same scope,
conditionally, so the equivalent in Elisp today will compose several
logic and pcase expressions, and this may lead to some repetitive
binding until the natural flow of the logic is discovered.  While
cond* seems capable of short-circuiting this repetition by propagating
binds, I am less clear on natural cases where I want to bind from an
inner rather than outer form.  Compared to let*, the existence of many
inner macro calls makes me wonder what I am compressing out.  Why not
a sequential match* for such a composition of dependent destructuring
binds and then no repetition of match* in the inner forms?

In fact, I fear the use of cond* for its expression of both logic and
destructuring within one macro will obfuscate the purpose of why it
was employed.  It does almost everything.  As for what is not
included, it can't be used for looping except as an inner form.  How
intensely jealous I am of Clojure, Rust, and Python, where
destructuring is quite natural in the other elementary forms,
including iteration, logic, and binding.

I do sense some idea trying to get out.  If the destructuring form is
not understood by let, let*, if-let* etc, then we are forced to choose
between destructuring and logic.  Other languages are blessed with
both and profit greatly from it.  Cond does seem to be reaching
towards such a composition.

One idea I like from cond* (at least described in the documentation
that I was unable to find in the implementation of) is this notion
that the form is limited to comprehending other symbols bearing the
"cond-star property" as clauses.

It all makes me intensely jealous of languages for which every
conditional binding or iterative binding form can also destructure.
What a magical world if let, while-let, and if-let could destructure
and if let itself could be composed with a conditional expression
without a distinct macro such as the one named if-let.

If only there was a tool to dispense with redundant parentheses around
inner calls to further macros.



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

* Re: cond* Examples
  2025-01-06 14:55 cond* Examples Psionic K
@ 2025-01-09  3:51 ` Richard Stallman
  2025-01-09 13:35   ` Trevor Arjeski
  0 siblings, 1 reply; 3+ messages in thread
From: Richard Stallman @ 2025-01-09  3:51 UTC (permalink / raw)
  To: Psionic K; +Cc: emacs-devel

[[[ To any NSA and FBI agents reading my email: please consider    ]]]
[[[ whether defending the US Constitution against all enemies,     ]]]
[[[ foreign or domestic, requires you to follow Snowden's example. ]]]

Thanks for your comments.

  > This feels inconsistent with `when-let*' and `if-let*' behavior, where
  > there's an intuitive reason that every bind must succeed for the first
  > or all body forms to evaluate.  Is this a first-pass tradeoff?

What does "first-pass tradeoff" mean?

I never considered the possibility of checking each of the binding values.
That would be convenient when you want to test them all.
It would ne a nuisance when there is a variable that could be nil
and you don't want to test it.

I don't have strong feelings about this.  Anyone who has an opinion
about this, please post it.

Regarding the general comments about other languages, I am indeed
trying to find a more elegant way to address the same issue that pcase
addresses.  But doing this in Lisp is constrained by the
characteristic of Lisp that is its strongest feature: that code is
data built out of simple general-purpose data structures.  This is the
furtherst step in that dieection that I saw a way to realize.

-- 
Dr Richard Stallman (https://stallman.org)
Chief GNUisance of the GNU Project (https://gnu.org)
Founder, Free Software Foundation (https://fsf.org)
Internet Hall-of-Famer (https://internethalloffame.org)





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

* Re: cond* Examples
  2025-01-09  3:51 ` Richard Stallman
@ 2025-01-09 13:35   ` Trevor Arjeski
  0 siblings, 0 replies; 3+ messages in thread
From: Trevor Arjeski @ 2025-01-09 13:35 UTC (permalink / raw)
  To: Richard Stallman; +Cc: Psionic K, emacs-devel


No constructive comments from me, but this thread got me tinkering with `cond*'.

I'm embarassed to say that I spent too much time on this little snippet:

(cond*
 ((bind* (foo 1)
         (bar 2)) :non-exit)
 ((eq foo bar) :eq)
 ((< foo bar) :lt)
 (t :nothing))

In my mind, this should return `:lt', but it returns `:nothing'. After scratching my head, I looked
at the code and it seems that it conflicts with the manual entry:

"If a clause has only one element, or if its first element is ‘t’,
 or if it ends with the keyword ‘:non-exit’, then this clause never
 exits the ‘cond*’ construct."

Apparently, if a clause returns any keyword, it does a passthrough (see `cond*-non-exit-clause-p' ?)

Changing to this works:

(cond*
 ((bind* (foo 1)
         (bar 2)) :non-exit) ; works with and without :non-exit
 ((eq foo bar) "eq")
 ((< foo bar) "lt")
 (t :nothing))
;; returns "lt"

Anyway, sorry if this is off-topic.




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

end of thread, other threads:[~2025-01-09 13:35 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-01-06 14:55 cond* Examples Psionic K
2025-01-09  3:51 ` Richard Stallman
2025-01-09 13:35   ` Trevor Arjeski

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