emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Code for cond*


From: JD Smith
Subject: Re: Code for cond*
Date: Tue, 23 Jan 2024 23:49:06 -0500

In general I’ve always felt cond by itself was missing some functionality and 
could really use the ability to make bindings — a cond-let if you will.  Even 
making and testing bindings local to each cond clause would be a real 
advance[1].   Pattern matching adds another level of complexity I hadn’t 
considered, but overall I'm supportive of efforts to improve cond.

I’ve used pcase lightly, but to be honest have always winced when I have 
encountered it in code I really need to understand.  As others have already 
discussed, the similar looking but in fact semantically distinct domain 
language has always thrown me (unlike cl-loop, whose language is not easily 
mistaken for regular elisp syntax).

But oddly enough, this thread discussing its potential replacement has given me 
the key insight — “imagine running list interpolation backwards”.  With that 
mental model, I find I can now read pcase forms much more easily and 
confidently.  A short introductory paragraph in the elisp pcase documentation 
which explains this approach to its novel syntax would have gone a long way for 
me.

> On Jan 23, 2024, at 1:10 PM, Stefan Monnier via Emacs development 
> discussions. <emacs-devel@gnu.org> wrote:
> 
>>       ((match* `(expt ,foo ,bar) x))
>>       ;; Bindings continue in effect.
> 
> Same comment as before: I like having both "bindings for the clause"
> and "bindings for the rest", but having the two be syntactically
> identical will lead to confusion.

This is a significant concern.  If I’m understanding the design correctly:

(let ((var outer-value))
  (cond*
   ((bind* (var (some-complicated-function-that-returns-nil))))
   ((bind* (other-var (some-other-function))))
   <... lots of other clauses>
   (t var)))

would return a different final fall-back value than the nearly identical form:

(let ((var outer-value))
  (cond*
   ((bind* (var (some-complicated-function-that-returns-nil))) t) ; <-- hidden 
danger
   ((bind* (other-var (some-other-function))))
   <... lots of other clauses>
   (t var)))

with a single `t’ added, perhaps a page above, deep inside a sibling clause.  
That kind of “spooky action at a distance” would in my view lead to hard to 
track down errors and difficult to parse code. 

Perhaps let* could be used for binding variables within clauses, and bind* for 
bindings which remain in effect for the rest of the construct, ignoring any 
final value given in the ((bind* )) clause.  But even with that disambiguation, 
I’m so used to “looking upwards to a parent let-form for the active bindings” 
that establishing bindings in a sibling clause like this would take some 
getting used to.

[1] I regularly convince myself that it’s such low hanging fruit, there must in 
fact already BE a cond-let, and I go hunting for it.  The obvious interface 
seems like such a straightforward extension of if/when-let, that there would be 
absolutely nothing new to learn:

(cond-let
 (((var value)
   (dvar (derived-from var))
   ((has-the-right-stuff-p dvar)))
  (cons 'correct dvar))

 (((foo value2)
   (bar (1- foo))
   ((< bar 0)))
  (cons 'incorrect bar))

 (t nil))




reply via email to

[Prev in Thread] Current Thread [Next in Thread]