all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
* Code for cond*
@ 2024-01-18  3:37 Richard Stallman
  2024-01-18  4:59 ` Emanuel Berg
                   ` (3 more replies)
  0 siblings, 4 replies; 134+ messages in thread
From: Richard Stallman @ 2024-01-18  3:37 UTC (permalink / raw)
  To: 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. ]]]

Here is the first draft of cond*.  I have tested some cases
but I ask others to help in testing it more thoroughly.

I invite constructive comments, bug reports, patches,
and suggestions.

First, here is the draft documentation.


A cond* clause is a "non-exit" clause if it (1) starts with t, (2) has
only one element, or (3) has a keyword as the first or last element.
After a non-exit clause finishes, control proceeds to the following
clause.

All other clauses are like cond clauses, in that when the condition is
true, it executes its clause body and then exits the cond*.

  (cond* 
       ;; Same as a clause in `cond',
       (CONDITION
        DO-THIS-IF-TRUE-THEN-EXIT...)

       ;; Execute FORM, and ignore its value
       ;; (except if this is the last clause).
       (FORM)

       ;; Variables to bind, as in let*, around the rest
       ;; of the cond*.
       ((bind* (x foobar) y z (foo 5) a))
       ;; Bindings continue in effect for the whole cond* construct.

       ;; Bind variables for the clause, as in let*.
       ((bind* (foo 5) (condition (hack-it foo)))
        ;; condition is that the last variable bound be non-nil.
        BODY...)
       ;; Bindings no longer in effect.

       ;; Extracts substructure and binds variables for the clause.
       ((match* `(expt ,foo ,bar) x)
        DO-THIS-IF-IT-MATCHED-THEN-EXIT...)
       ;; Bindings no longer in effect.

       ;; Extracts substructure and binds variables
       ;; for the rest of the cond*.
       ;; Like above but always falls thru to next clause.
       ;; All the variables mentioned in the pattern
       ;; are bound whether match succeeds or not.
       ;; If a value can be determined from an incomplete match,
       ;; the variable gets that value.
       ((match* `(expt ,foo ,bar) x))
       ;; Bindings continue in effect.

       ;; Another example of fall-through match* clause.
       ;; All the variables mentioned in the pattern
       ;; are bound in all cases.
       ((match* (or `() `(,macroexp-const-p const)) body)

;; The `match-set*' is a tentative proposal.  It may not be worth including.
       ;; Extracts substructure and sets variables if match succeeds
       ((match-set* `(expt ,foo ,bar) x)
        DO-THIS-IF-IT-MATCHED-THEN-EXIT...)

       ;; Extracts substructure and sets variables without binding them.
       ;; Always falls thru to next clause.
       ((match-set* `(expt ,foo ,bar) x))
       )

To execute several expressions and not exit, use this:

       ((progn DO-THIS-UNCONDITIONALLY-AND-DONT-STOP...)

To test CONDITION and return its value if non-nil, use this:

       (CONDITION CONDITION)

That is the best way when CONDITION is simple.  When it is complex,
this may be clearer and shorter than repeating CONDITION:

       ((bind* (value CONDITION)) value)
\f
**Possible types of patterns for match*.

CONSTANT: nil, t, a keyword, a quoted constant,
          fixed parts of a backquote.

  This matches any value equal to CONSTANT.

_

  _ means to match any value and not store it anywhere.

VARIABLE: a symbol

  A symbol means to match any value and set VARIABLE to it.

MACRO-CALL: a list which is a call to a macro.

  The macro call will be expanded, then the expansion
  used as a pattern.

A vector

  Each element is a subpattern.  This
  matches a vector of data if each element of that vector is
  matched by the corresponding subpattern.

A backquoted cons cell

  The car and the cdr are subpattenrs.  The cons cell pattern
  matches any cons cell whose car and cdr match those two subpatterns.
  How nil as a cdr matches is controlled by the nil-match-all flag.

  When the nil-match-all flag is false, nil as a cdr matches
  only nil itself.

  When the nil-match-all flag is true, nil as a cdr matches
  any object and ignores that object.

  The nil-match-all flag is false by default.  The `cdr' pattern maks
  it false within its its subpattern, and the `cdr-safe' pattern makes
  it true forwithin its its subpattern.

  The nil-match-all flag has no effect on subpatterns other
  than backquoted cons cells.

(cdr-safe BACKQUOTED-CONS-CELL)

  This pattern is equivalent to BACKQUOTED-CONS-CELL by itself
  except that it makes the nil-match-all flag true within it.

     (cdr-safe `(a b))  matches (a b), (a b c), (a b . d), etc.

  The nil-match-all flag has no effect on subpatterns other
  than backquoted cons cells.

(cdr BACKQUOTED-CONS-CELL)

  This pattern is equivalent to QUOTED-CONS-CELL by itself
  except that it makes the nil-match-all flag false within it.

     (cdr `(a b))  matches only (a b).

A string

  The string is interpreted as a regular expression and can match
  against a data object if the object is a string.

  If you want to match exactly the string `foo', you can write a
  regexp which matches only that string.

     "foo\\>"

  Or you can evaluate (regexp-quote "foo"), add "\>" at the end, and
  copy that string into your pattern.

  Or you can write a constrained variable pattern using the string
  itself, like this:

     (equal foo "xyz")

(rx RX-PATTERN)

  This is another way of specifying a regular expression,
  using the Lisp-like syntax that the `rx' function uses.

(rx RX-PATTERN VARS...)

  Like the basic`rx' pattern except that it binds the variables
  VARS to the matched substrings.  (nth 0 VARS) is bound to
  substring 0 (the whole match),   (nth 0 VARS) is bound to
  substring 1 as specified in RX-PATTERN, and so on.
  Since this is implemented using `string-match', you can't
  use more substrings than `string-match' itself supports.

A backquoted structure constructor

  Each field is a subpattern.  This matches a structure as data if
  each field element of that structure is matched by the corresponding
  subpattern.

  This is not yet implemented.

Alternatives: (or SUBPATTERNS...)

  This tries to match each of the SUBPATTERNS in order until one matches.
  If the pattern is being used to bind variables, it binds all the variables
  specified in any of SUBPATTERNS.

Conjunction: (and SUBPATTERNS...)

  This tries to match each of the SUBPATTERNS in order.  It succeeds
  in matching if every one of the SUBPATTERNS matches.

  If the pattern is being used to bind variables, it binds all the variables
  specified in any of SUBPATTERNS.  If one of the SUBPATTERNS tries to bind
  a variable and that variable has already been bound in thhis patter,
  it insists on the same value.

Constrained variable: (PRED VARIABLE)  or, more generally,
                      (PRED VARIABLE OTHER-ARGS...)

  This matches any value VALUE that satisfies the specified
  constraint.  PRED is a function to test the constraint.  It receives
  VALUE as its first argument.  If PRED returns true, that means VALUE
  satisfies the constraint, so this pattern binds (or sets) VARIABLE
  to that value.  For instance,

     (symbolp sym)   ; Match any symbol, bind `sym' to it.

  If you wish, you can specify additional arguments to pass to PRED.
  The OTHER-ARGS are not patterns to match, they are Lisp expressions
  whose values specify the additional arguments to pass to PRED,
  following the first argument which is VALUE.  In effect, the call
  to PRED looks like this:

      (apply PRED VALUE (mapcar 'eval OTHER-ARGS))

  Here are two examples of passing an additional argument.
 
     (> num-foos 1)  ; Match any number greater than 1, bind `num-foos' to it.
     (>= num-foos min-foos)  ; Match any number not less than MIN-FOOS,
                            ;  bind `num-foos' to it.

  It is often useful to make a conjunction of constrained variable patterns..
  For instance, this matches and binds `num-foos' provided the value to
  match is an integer and not less than min-num-foos.

     (and (integerp num-foos) (>= num-foos min-num-foos))

  When matching to bind variables, the presence of a constrained
  variable pattern, as a subpattern of the overall pattern to be
  matched, unconditionally binds VARIABLE whether the subpattern
  matches or not.

  Errors in constrained variable constructs:

  If an error occurs in testing the constraint, or calculating 
  the other arguments to pass to the predicate, that pattern fails
  to match but does not terminate the cond* form.

General constrained variable: (constrain VAR EXPRESSION)

  This general constrained variable pattern binds VAR to the
  value being matched against, tentatively, then evaluates EXPRESSION.
  If the result is true, the match succeeds and leaves VAR
  bound to that value.

  For instance,

    (constrain x (and (> x 0) (< x 100)))

  succeeds if the value being matched aainst is in the open interval (0, 100),
  and in that case it binds x to that value.

\f
;;; ??? Should use use byte-compile-warn-x.

;; Copyright (C) 1985-2024 Free Software Foundation, Inc.

;; Maintainer: emacs-devel@gnu.org
;; Keywords: abbrev convenience
;; Package: emacs

;; This file is cond*,  not yet part of GNU Emacs.

;; cond* is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; cond* is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.

(defmacro cond* (&rest clauses)
;;;??? Doc string will go here.
  (cond*-convert clauses))

(defun cond*-non-exit-clause-p (clause)
  "If CLAUSE, a cond* clause, is a non-exit clause, return t."
  (or (null (cdr-safe clause))   ;; clause has only one element.
      (and (cdr-safe clause)
           ;; Starts with t.
           (or (eq (car clause) t)
               ;; Begins with keyword.
               (keywordp (car clause))))
      ;; Ends with keyword.
      (keywordp (car (last clause)))))

(defun cond*-non-exit-clause-substance (clause)
  "For a non-exit cond* clause CLAUSE, return its substance.
This removes a final keyword if that's what makes CLAUSE non-exit."
  (cond ((null (cdr-safe clause))   ;; clause has only one element.
         clause)
        ;; Starts with t or a keyword.
        ;; Include t as the first element of the substancea
        ;; so that the following element is not treated as a pattern.
        ((and (cdr-safe clause)
              (or (eq (car clause) t)
                  (keywordp (car clause))))
         ;; Standardize on t as the first element.
         (cons t (cdr clause)))

        ;; Ends with keyword.
        ((keywordp (car (last clause)))
         ;; Do NOT include the final keyword.
         (butlast clause))))

(defun test-cond*-non-exit-clause-p ()
  ;; Should return (nil nil t t t t).
  (list
   (cond*-non-exit-clause-p '((memq foo list) (setq foo 1)))
   (cond*-non-exit-clause-p '(nil (setq foo 1)))
   (cond*-non-exit-clause-p '((setq foo 1)))
   (cond*-non-exit-clause-p '(t (setq foo 1)))
   (cond*-non-exit-clause-p '(:non-exit (setq foo 1)))
   (cond*-non-exit-clause-p '((setq foo 1) :non-exit))))

(defun cond*-convert (clauses)
  "Process a list of cond* clauses, CLAUSES.
Returns the equivalent Lisp expression."
  (if clauses
      (cond*-convert-clause (car-safe clauses) (cdr-safe clauses))))

(defun cond*-convert-clause (clause rest)
  "Process one `cond*' clause, CLAUSE.
REST is the rest of the clauses of this cond* expression."
  (if (cond*-non-exit-clause-p clause)
      ;; Handle a non-exit clause.  Make its bindings active
      ;; around the whole rest of this cond*, treating it as
      ;; a condition whose value is always t, around the rest
      ;; of this cond*.
      (let ((substance (cond*-non-exit-clause-substance clause)))
        (cond*-convert-condition
         ;; Handle the first substantial element in the non-exit clause
         ;; as a matching condition.
         (car substance)
         ;; Any following elements in the
         ;; non-exit clause are just expressions.
         (cdr substance)
         ;; Remaining clauses will be UNCONDIT-CLAUSES:
         ;; run unconditionally and handled as a cond* body.
         rest
         nil nil))
    ;; Handle a normal (conditional exit) clauss.
    (cond*-convert-condition (car-safe clause) (cdr-safe clause) nil
                             rest (cond*-convert rest))))

(defun cond*-convert-condition (condition true-exps uncondit-clauses rest iffalse)
  "Process the condition part of one cond* clause.
TRUE-EXPS is a list of Lisp expressions to be executed if this
condition is true, and inside its bindings.
UNCONDIT-CLAUSES is a list of cond*-clauses to be executed if this
condition is true, and inside its bindings.
This is used for non-exit clauses; it is nil for conditional-exit clauses.

REST and IFFALSE are non-nil for conditional-exit clauses that are not final.
REST is a list of clauses to process after this one if
this one could have exited but does not exit.
This is used for conditional exit clauses.
IFFALSE is the value to compute after this one if
this one could have exited but does not exit.
This is used for conditional exit clauses."
  (if (and uncondit-clauses rest)
      (error "Clase is both exiting and non-exiting-nil"))
  (let ((pat-type (car-safe condition)))
    (cond ((eq pat-type 'bind*)
           ;; When a bind* needs to be tested as a condition,
           ;; which is whenever that clause has elements after
           ;; the bind* element itself, the condition value
           ;; is the value of the last binding made.
           (let* ((lastbinding
                   ;; The last binding.
                   (car-safe (last condition)))
                  (last-value
                   ;; The initial value specified in the last binding.
                   (if (symbolp lastbinding) nil
                     (car-safe (cdr-safe lastbinding)))))
             (if rest
                 ;; bind* starts an exiting clause which is not final.
                 `(if ,last-value
                      (let* ,(cdr condition)
                        . ,true-exps)
                    ,iffalse)
               (if uncondit-clauses
                   ;; bind* starts a non-exit clause.
                   ;; Run the TRUE-EXPS.
                   ;; Then always go on to run the UNCONDIT-CLAUSES.
                   `(progn
                      (if ,last-value
                          (let* ,(cdr condition)
                            . ,true-exps))
                      (let* ,(cdr condition)
                        ,(cond*-convert uncondit-clauses)))
                 ;; bind* starts an exiting clause which is final.
                 ;; If there are TRUE-EXPS, run them if condition succeeded.
                 ;; Always make the bindings, in case the
                 ;; initial values have side effects.
                 `(if ,last-value
                      (let* ,(cdr condition)
                        . ,true-exps))))))
          ((eq pat-type 'match*)
           (cond*-match condition true-exps uncondit-clauses iffalse))
          (t
           ;; Ordinary Lixp expression is the condition 
           (if rest
               ;; A nonfinal exiting clause.
               ;; If condition succeeds, run the TRUE-EXPS.
               ;; There are following clauses, so run IFFALSE
               ;; if the condition fails.
               `(if ,condition
                    (progn . ,true-exps)
                  ,iffalse)
             (if uncondit-clauses
                 ;; A non-exit clause.
                 ;; If condition succeeds, run the TRUE-EXPS.
                 ;; Then always go on to run the UNCONDIT-CLAUSES.
                 `(progn (if ,condition
                             (progn . ,true-exps))
                         ,(cond*-convert uncondit-clauses))
               ;; An exiting clause which is also final.
               ;; If there are TRUE-EXPS, run them if CONDITION succeeds.
               (if true-exps
                   `(if ,condition (progn . ,true-exps))
                 ;; Run and return CONDITION.
                 condition)))))))
\f
(defun cond*-match (matchexp true-exps uncondit-clauses iffalse)
  "Generate code to match a match* pattern PATTERN.
Match it against data represented by the expression DATA.
TRUE-EXPS, UNCONDIT-CLAUSES and IFFALSE have the same meanings
as in `cond*-condition'."
  (when (or (null matchexp) (null (cdr-safe matchexp))
            (null (cdr-safe (cdr matchexp)))
            (cdr-safe (cdr (cdr matchexp))))
    (error "Malformed (match* ...) expression"))
  (let* (raw-result
         (pattern (nth 1 matchexp))
         (data (nth 2 matchexp))
         expression
         (inner-data data)
         ;; Add backtrack aliases for or-subpatterns to cdr of this.
         (backtrack-aliases (list nil))
         gensym)
    ;; For now, always bind a gensym to the data to be matched.
    (setq gensym (gensym "d") inner-data gensym)
    ;; Process the whole pattern as a subpattern.
    (setq raw-result (cond*-subpat pattern nil nil backtrack-aliases inner-data))
    (setq expression (cdr raw-result))
    ;; Run TRUE-EXPS if match succeeded.  Bind our bindings around it.
    (setq expression
          `(if ,expression
               ,(if (not (and backtrack-aliases (null uncondit-clauses)))
                    ;; Bind these here if there are no UNCONDIT-CLAUSES.
                    `(let ,(mapcar 'cdr (cdr backtrack-aliases)
                       (let* ,(car raw-result)
                        ,@true-exps)))
                  `(let* ,(car raw-result)
                     ,@true-exps))
             ;; For a non-final exiting clause, run IFFALSE if match failed.
             ;; Don't bind the bindings for following clauses
             ;; since an exiting clause's bindings don't affect later clauses.
             ,iffalse))
    ;; For a non-final non-exiting clause,
    ;; always run the UNCONDIT-CLAUSES.
    (if uncondit-clauses
        (setq expression
              `(progn ,expression 
                      (let* ,(car raw-result)
                        ,(cond*-convert uncondit-clauses)))))
    ;; If there are backtrack aliases, bind them around the UNCONDIT-CLAUSES.
    (if (and backtrack-aliases uncondit-clauses)
      (setq expression `(let ,(mapcar 'cdr (cdr backtrack-aliases))
                          ,expression)))
    ;; If we used a gensym, add code to bind it.
    (if gensym
        `(let ((,gensym ,data)) ,expression)
      expression)))

(defun cond*-bind-around (bindings exp)
  "Wrap a `let*' around EXP, to bind those of BINDINGS used in EXP."
  `(let* ,(nreverse (cond*-used-within bindings exp)) ,exp))

(defun cond*-used-within (bindings exp)
  "Return the list of those bindings in BINDINGS which EXP refers to.
This operates naively and errs on the side of overinclusion,
and does not distinguish function names from variable names.
That is safe for the purpose this is used for."
  (cond ((symbolp exp) 
         (let ((which (assq exp bindings)))
           (if which (list which))))
        ((listp exp)
         (let (combined (rest exp))
           (while rest
             (let ((in-this-elt (cond*-used-within bindings (car rest))))
               (while in-this-elt
                 (unless (assq (car-safe in-this-elt) combined)
                   (push (car-safe in-this-elt) combined))
                 (pop in-this-elt)))
             (pop rest))
           combined))))

;;; ??? Structure type patterns not implemented yet.
;;; ??? Probably should optimize the `nth' calls in handling `list'.

(defun cond*-subpat (subpat cdr-safe bindings backtrack-aliases data)
  "Generate code to match ibe subpattern within `match*'.
SUBPAT is the subpattern to handle.
CDR-SAFE if true means don't verify there are no extra elts in a list.
BINDINGS is the list of bindings made by
the containing and previous subpatterns of this pattern.
Each element of BINDINGS must have the frm (VAR VALUE).
BACKTRACK-ALIASES is used to pass adta uward.  Initial call should
pass (list).  The cdr of this collects backtracking aliases made for
variables boung within (or...) patterns so that the caller
dna bind them etc.
DATA is the expression for the data that this subpattern is
supposed to match against.

Return Value has the form (BINDINGS . CONDITION), where
BINDINGS is the list of bindings to be made for SUBPAT
plus the subpatterns that contain/precede it.
Each element of BINDINGS has the form (VAR VALUE).
CONDITION is the condition to be tested to decide
whether SUBPAT (as well as the subpatterns that contain/precede it) matches,"
  (cond ((eq subpat '_)
         ;; _ as pattern makes no bindings and matches any data.
         (cons bindings t))
        ((symbolp subpat)
         ;; Bind or match a symbol to this data
         (let ((this-binding (assq subpat bindings)))
           (if this-binding
               ;; Variable already bound.
               ;; Compare what this variable should be bound to
               ;; to the fata it is supposed to match.
               ;; That is because we don't actually bind thes bindings
               ;; around the condition-testing expression.
               (cons bindings `(equal ,(cdr this-binding) ,data))
             ;; Inside or subpattern, if this symbol already has an alias
             ;; for backtracking, just use that.
             (let ((this-alias (assq subpat (cdr backtrack-aliases))))
               (if this-alias (cdr this-alias)
                 (if backtrack-aliases
                     ;; Inside or subpattern but this symbol has no alias,
                     ;; make one for it.
                     (progn (setcdr backtrack-aliases (cons (cons subpat (gensym "ba"))
                                                            (cdr backtrack-aliases)))
                            ;; Init the binding to symbol's backtrack-alias
                            ;; and set the alias to nil.
                            (cons `((,subpat ,(cdar (cdr backtrack-aliases))) . ,bindings)
                                  t                                  ))
                   (cons `((,subpat ,data) . ,bindings)
                         t)))))))
;;; This is not true any more.
;;;         ;; Actually we bind it to nil at the start of the clause
;;;         ;; and set it to the matched value if it matches.
;;;         (cons `((,subpat nil) . ,bindings)
;;;               `(progn (setq ,subpat ,data) t)))
        ;; Various constants.
        ((numberp subpat)
         (cons bindings `(eql ,subpat ,data)))
        ((keywordp subpat)
         (cons bindings `(eq ,subpat ,data)))
        ((memq subpat '(nil t))
         (cons bindings `(eq ,subpat ,data)))
        ;; Regular expressions as strings.
        ((stringp subpat)
         (cons bindings `(string-match ,(concat subpat "\\>") ,data)))
        ;; All other atoms match with `equal'.
        ((not (consp subpat))
         (cons bindings `(equal ,subpat ,data)))
        ((not (consp (cdr subpat)))
         (error "%s subpattern malformed or missing arguments" (car suboat)))
        ;; Regular expressions specified as list structure.
        ;; (rx REGEXP VARS...)
        ((eq (car subpat) 'rx)
         (let* ((rxpat (concat (funcall 'rx (cadr subpat)) "\\>"))
                (vars (cddr subpat)) setqs (varnum 0)
                (match-exp `(string-match ,rxpat ,data)))
           (if (null vars)
               (cons bindings match-exp)
             ;; There are variables to bind to the matched substrings.
             (if (> (length vars) 10)
                 (error "Too many variables specified for matched substrings"))
             (dolist (elt vars)
               (unless (symbolp elt)
                 (error "Non-symbol %s given as name for matched substring" elt)))
             ;; Bind these variables to nil, before the pattern.
             (setq bindings (nconc (mapcar 'list vars) bindings))
             ;; Make the expressions to set the variables.
             (setq setqs (mapcar
                          (lambda (var)
                            (prog1 `(setq ,var (match-string ,varnum ,data))
                              (setq varnum (1+ varnum))))
                          vars))
             (cons bindings `(if ,match-exp
                                 (progn ,@setqs t))))))
        ;; Quoted object as constant to match with `equal'.
        ((eq (car subpat) 'quote)
         (cons bindings `(equal ,subpat ,data)))
        ;; Match a call to `cons' by destructuring.
        ((eq (car subpat) 'cons)
         (let (car-result cdr-result car-exp cdr-exp)
           (setq car-result
                 (cond*-subpat (nth 1 subpat) cdr-safe bindings backtrack-aliases `(car ,data)))
           (setq bindings (car car-result)
                 car-exp (cdr car-result))
           (setq cdr-result
                 (cond*-subpat (nth 2 subpat) cdr-safe bindings backtrack-aliases `(cdr ,data)))
           (setq bindings (car cdr-result)
                 cdr-exp (cdr cdr-result))
           (cons bindings
                 `(and ,car-exp ,cdr-exp))))
        ;; Match a call to `list' by destructuring.
        ((eq (car subpat) 'list)
         (let ((i 0) expressions)
           ;; Check for bad structure of SUBPAT here?
           (dolist (this-elt (cdr subpat))
             (let ((result 
                    (cond*-subpat this-elt cdr-safe bindings backtrack-aliases `(nth ,i ,data))))
               (setq i (1+ i))
               (setq bindings (car result))
               (push (cdr result) expressions)))
           ;; Verify that list ends here, if we are suppose to check that.
           (unless cdr-safe
             (push `(null (nthcdr ,i ,data)) expressions))
           (cons bindings `(and . ,(nreverse expressions)))))
        ;; Match a call to `vector' by destructuring.
        ((eq (car subpat) 'vector)
         (let ((length (length vector)) (vector (cadr subpat))
               (i 0) expressions)
           (dotimes (i length)
             (let* ((this-elt (aref i vector))
                    (result 
                     (cond*-subpat (aref i vector) cdr-safe
                                   bindings backtrack-aliases `(aref ,i ,data))))
               (setq i (1+ i))
               (setq bindings (car result))
               (push (cdr result) expressions)))
           (cons bindings `(and . ,(nreverse expressions)))))
        ;; Subpattern to set the cdr-safe flag
        ((eq (car subpat) 'cdr-safe)
         (cond*-subpat (cadr subpat) t bindings backtrack-aliases data))
        ;; Subpattern to clear the cdr-safe flag
        ((eq (car subpat) 'cdr)
         (cond*-subpat (cadr subpat) nil bindings backtrack-aliases data))
        ;; Handle conjunction subpatterns.
        ((eq (car subpat) 'and)
         (let (expressions)
           ;; Check for bad structure of SUBPAT here?
           (dolist (this-elt (cdr subpat))
             (let ((result 
                    (cond*-subpat this-elt cdr-safe bindings backtrack-aliases data)))
               (setq bindings (car result))
               (push (cdr result) expressions)))
           (cons bindings `(and . ,(nreverse expressions)))))
        ;; Handle disjunction subpatterns.
        ((eq (car subpat) 'or)
         ;; The main complexity is unsetting the pattern variables
         ;; that will not have matched.
         (let (expressions)
           ;; Check for bad structure of SUBPAT here?
           (dolist (this-elt (cdr subpat))
             (let* ((backtrack-aliases-before backtrack-aliases)
                    (result 
                     (cond*-subpat this-elt cdr-safe bindings backtrack-aliases data))
                    (bindings-before-or bindings)
                    bindings-to-clear expression)
               (setq bindings (car result))
               (setq expression (cdr result))
               ;; Were any bindings made by this arm of the disjunction?
               (when (not (eq bindings bindings-before-or))
                 ;; Ok, arrange to clear their backtrack aliases
                 ;; if this arm does not match.
                 (setq bindings-to-clear bindings)
                 (let (clearing)
                   ;; For each of those bindings,
                   (while (not (eq bindings-to-clear bindings-before-or))
                     ;; Make an expression to set it to nil, in CLEARING.
                     (let* ((this-variable (caar bindings-to-clear))
                            (this-backtrack (assq this-variable
                                                  (cdr backtrack-aliases))))
                       (push `(setq ,(cdr this-backtrack) nil) clearing))
                     (setq bindings-to-clear (cdr bindings-to-clear)))
                   ;; Wrap EXPRESSION to clear those backtrack aliases
                   ;; if EXPRESSION is false.
                   (setq expression
                         (if (null clearing)
                             ,expression
                           (if (null (cdr clearing))
                               `(or ,expression
                                    ,(car clearing))
                             (progn ,@clearing))))))
               (push expression expressions)))
           (cons bindings `(or . ,(nreverse expressions)))))
        ;; Expand cond*-macro call, treat result as a subpattern.
        ((get (car subpat) 'cond*-expander)
         ;; Treat result as a subpattern.
         (cond*-subpat (funcall (get (car subpat) 'cond*-expander) subpat)
                       cdr-safe bindings backtrack-aliases data))
        ((macrop (car subpat))
         (cond*-subpat (macroexpand subpat) cdr-safe bindings backtrack-aliases data))
        ;; Simple constrained variable, as in (symbolp x).
        ((functionp (car subpat))
         ;; Without this, nested constrained variables just worked.
;;;         (unless (symbolp (cadr subpat))
;;;           (error "Complex pattern nested in constrained variable pattern"))
         (let* ((rest-args (cddr subpat))
                ;; Process VAR to get a binding for it.
                (result (cond*-subpat (cadr subpat) cdr-safe bindings backtrack-aliases data))
                (new-bindings (car result))
                (expression (cdr result))
                (combined-exp
                 `(and (,(car subpat) ,data . ,rest-args) ,expression)))
           (cons new-bindings
                 (cond*-bind-around new-bindings combined-exp))))
        ;; Generalized constrained variable: (constrain VAR EXP)
        ((eq (car subpat) 'constrain)
         (unless (symbolp (cadr subpat))
           (error "Complex pattern nested in constrained variable pattern"))
         ;; Process VAR to get a binding for it.
         (let ((result (cond*-subpat (cadr subpat) cdr-safe bindings backtrack-aliases data)))
           (cons (car result)
                 ;; This is the test condition 
                 (cond*-bind-around (car result) (nth 2 subpat)))))
        (t (error "Undefined pattern type `%s' in `cond*'" (car subpat)))))

-- 
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] 134+ messages in thread

* Re: Code for cond*
  2024-01-18  3:37 Code for cond* Richard Stallman
@ 2024-01-18  4:59 ` Emanuel Berg
  2024-01-20  3:39   ` Richard Stallman
  2024-01-18 15:44 ` Andrea Corallo
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 134+ messages in thread
From: Emanuel Berg @ 2024-01-18  4:59 UTC (permalink / raw)
  To: emacs-devel

Richard Stallman wrote:

> Here is the first draft of cond* [...]

[ For easier access to the source, Gnus users can do
 `gnus-summary-stop-page-breaking'. ]

Here are what the byte-compiler reports [last].

You can also try `elint-current-buffer' and `native-compile',
they also report a couple of things that should be easy to
fix. Also do (checkdoc-current-buffer t) for some
docstrings fixes.

The docstring and comments have a couple of typos, those are
easy to find and correct with your favorite
Emacs spellchecker.

In toplevel form:
cond-rms.el:1:1: Warning: file has no ‘lexical-binding’
directive on its first line

In cond*-match:
cond-rms.el:202:29: Warning: ‘mapcar’ called with 3 arguments,
but accepts only 2

In cond*-subpat:
cond-rms.el:320:69: Warning: reference to free variable ‘suboat’
cond-rms.el:377:32: Warning: reference to free variable ‘vector’

In end of data:
cond-rms.el: Warning: the function ‘,’ is not known to be defined.
cond-rms.el: Warning: the function ‘,@’ is not known to be defined.

-- 
underground experts united
https://dataswamp.org/~incal




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

* Re: Code for cond*
  2024-01-18  3:37 Code for cond* Richard Stallman
  2024-01-18  4:59 ` Emanuel Berg
@ 2024-01-18 15:44 ` Andrea Corallo
  2024-01-19 10:42   ` João Távora
  2024-01-20  3:39   ` Richard Stallman
  2024-01-23 18:10 ` Stefan Monnier via Emacs development discussions.
  2024-01-24 12:39 ` Alan Mackenzie
  3 siblings, 2 replies; 134+ messages in thread
From: Andrea Corallo @ 2024-01-18 15:44 UTC (permalink / raw)
  To: Richard Stallman; +Cc: emacs-devel

Richard Stallman <rms@gnu.org> writes:

> [[[ 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. ]]]
>
> Here is the first draft of cond*.  I have tested some cases
> but I ask others to help in testing it more thoroughly.
>
> I invite constructive comments, bug reports, patches,
> and suggestions.
>
> First, here is the draft documentation.
>
>
> A cond* clause is a "non-exit" clause if it (1) starts with t, (2) has
> only one element, or (3) has a keyword as the first or last element.
> After a non-exit clause finishes, control proceeds to the following
> clause.
>
> All other clauses are like cond clauses, in that when the condition is
> true, it executes its clause body and then exits the cond*.
>
>   (cond* 
>        ;; Same as a clause in `cond',
>        (CONDITION
>         DO-THIS-IF-TRUE-THEN-EXIT...)
>
>        ;; Execute FORM, and ignore its value
>        ;; (except if this is the last clause).
>        (FORM)
>
>        ;; Variables to bind, as in let*, around the rest
>        ;; of the cond*.
>        ((bind* (x foobar) y z (foo 5) a))
>        ;; Bindings continue in effect for the whole cond* construct.

Hi Richard,

apologies if it was discussed already, wanted to ask: what is the reason
for some of these cond* clauses to keep the binding in effect outside
the clause itself and for the whole cond* construct?  At first glance it
doesn't look very idiomatic in Lisp terms to me.

Thanks!

  Andrea



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

* Re: Code for cond*
  2024-01-18 15:44 ` Andrea Corallo
@ 2024-01-19 10:42   ` João Távora
  2024-01-21  3:04     ` Richard Stallman
  2024-01-20  3:39   ` Richard Stallman
  1 sibling, 1 reply; 134+ messages in thread
From: João Távora @ 2024-01-19 10:42 UTC (permalink / raw)
  To: Andrea Corallo; +Cc: Richard Stallman, emacs-devel

On Thu, Jan 18, 2024 at 3:44 PM Andrea Corallo <acorallo@gnu.org> wrote:

> apologies if it was discussed already, wanted to ask: what is the reason
> for some of these cond* clauses to keep the binding in effect outside
> the clause itself and for the whole cond* construct?  At first glance it
> doesn't look very idiomatic in Lisp terms to me.

I think I asked the same, and Drew Adams and Adam Porter also
raised this point:

   https://lists.gnu.org/archive/html/emacs-devel/2023-12/msg00554.html
   https://lists.gnu.org/archive/html/emacs-devel/2023-12/msg00618.html

and I'm not sure we eventually clarified it.  Apologies if I missed
some email addressing it as I'm not following this discussion in
detail.

Also, since cond* does a strict subset of what pcase does, has any
thought been given to implementing 'cond*' _on top_ of 'pcase', like
some systems define 'cond' in terms of 'if' (or vice versa)?

Not meant as gratuitous provocation, really.  I think it'd have advantages:

* presumably every cond* feature can be supported (if indeed I'm right
  about the "strict subset")

* it would probably save a lot of repeated code

* it would make it easy for those who can already read pcase
  to just macroexpand the cond* form and see what it is actually
  doing underneath.

So could actually facilitate the adoption path for 'cond*' (as
questionable as that path may still be, at least for some parties).

João



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

* Re: Code for cond*
  2024-01-18 15:44 ` Andrea Corallo
  2024-01-19 10:42   ` João Távora
@ 2024-01-20  3:39   ` Richard Stallman
  1 sibling, 0 replies; 134+ messages in thread
From: Richard Stallman @ 2024-01-20  3:39 UTC (permalink / raw)
  To: Andrea Corallo; +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. ]]]

  > apologies if it was discussed already, wanted to ask: what is the reason
  > for some of these cond* clauses to keep the binding in effect outside
  > the clause itself and for the whole cond* construct?

One of the long-bemoaned inconveniences of cond is that often in
mid-cond one wants to bind some variables and then proceed with more
cond clauses.  Lisp programmers have complained about this for
decades.  Sure, you can do

  (t (let ((temp (cadr whatsis)))
       (cond ((eq temp 'foo))
             ...

but this  increases the indentation by 11 columns.
bind* gives the same effect without deeper nesting.

Consider the definition of cond*-subpat.  If I rewrite that using
cond*, I would bind a variable to (car subaat) after the clauses that
deal with atomic subpatterns.

-- 
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] 134+ messages in thread

* Re: Code for cond*
  2024-01-18  4:59 ` Emanuel Berg
@ 2024-01-20  3:39   ` Richard Stallman
  2024-01-24 12:37     ` Po Lu
  2024-01-24 19:12     ` Alan Mackenzie
  0 siblings, 2 replies; 134+ messages in thread
From: Richard Stallman @ 2024-01-20  3:39 UTC (permalink / raw)
  To: Emanuel Berg; +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.  I fixed the bugs you mentioned; elint did not show any
others.  I always ran it interpreted to facilitate debugging, so I had
never compiled it.

Here is the new version.

To test it thoroughly is more that I can try to do.  Could people please
try using cond*?


;;; -*-lexical-binding: t; -*-
;;; ??? Should use use byte-compile-warn-x.

;; Copyright (C) 1985-2024 Free Software Foundation, Inc.

;; Maintainer: emacs-devel@gnu.org
;; Keywords: abbrev convenience
;; Package: emacs

;; This file is cond*,  not yet part of GNU Emacs.

;; cond* is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; cond* is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.

(defmacro cond* (&rest clauses)
;;;??? Doc string will go here.
  (cond*-convert clauses))

(defun cond*-non-exit-clause-p (clause)
  "If CLAUSE, a cond* clause, is a non-exit clause, return t."
  (or (null (cdr-safe clause))   ;; clause has only one element.
      (and (cdr-safe clause)
           ;; Starts with t.
           (or (eq (car clause) t)
               ;; Begins with keyword.
               (keywordp (car clause))))
      ;; Ends with keyword.
      (keywordp (car (last clause)))))

(defun cond*-non-exit-clause-substance (clause)
  "For a non-exit cond* clause CLAUSE, return its substance.
This removes a final keyword if that's what makes CLAUSE non-exit."
  (cond ((null (cdr-safe clause))   ;; clause has only one element.
         clause)
        ;; Starts with t or a keyword.
        ;; Include t as the first element of the substancea
        ;; so that the following element is not treated as a pattern.
        ((and (cdr-safe clause)
              (or (eq (car clause) t)
                  (keywordp (car clause))))
         ;; Standardize on t as the first element.
         (cons t (cdr clause)))

        ;; Ends with keyword.
        ((keywordp (car (last clause)))
         ;; Do NOT include the final keyword.
         (butlast clause))))

(defun test-cond*-non-exit-clause-p ()
  ;; Should return (nil nil t t t t).
  (list
   (cond*-non-exit-clause-p '((memq foo list) (setq foo 1)))
   (cond*-non-exit-clause-p '(nil (setq foo 1)))
   (cond*-non-exit-clause-p '((setq foo 1)))
   (cond*-non-exit-clause-p '(t (setq foo 1)))
   (cond*-non-exit-clause-p '(:non-exit (setq foo 1)))
   (cond*-non-exit-clause-p '((setq foo 1) :non-exit))))

(defun cond*-convert (clauses)
  "Process a list of cond* clauses, CLAUSES.
Returns the equivalent Lisp expression."
  (if clauses
      (cond*-convert-clause (car-safe clauses) (cdr-safe clauses))))

(defun cond*-convert-clause (clause rest)
  "Process one `cond*' clause, CLAUSE.
REST is the rest of the clauses of this cond* expression."
  (if (cond*-non-exit-clause-p clause)
      ;; Handle a non-exit clause.  Make its bindings active
      ;; around the whole rest of this cond*, treating it as
      ;; a condition whose value is always t, around the rest
      ;; of this cond*.
      (let ((substance (cond*-non-exit-clause-substance clause)))
        (cond*-convert-condition
         ;; Handle the first substantial element in the non-exit clause
         ;; as a matching condition.
         (car substance)
         ;; Any following elements in the
         ;; non-exit clause are just expressions.
         (cdr substance)
         ;; Remaining clauses will be UNCONDIT-CLAUSES:
         ;; run unconditionally and handled as a cond* body.
         rest
         nil nil))
    ;; Handle a normal (conditional exit) clauss.
    (cond*-convert-condition (car-safe clause) (cdr-safe clause) nil
                             rest (cond*-convert rest))))

(defun cond*-convert-condition (condition true-exps uncondit-clauses rest iffalse)
  "Process the condition part of one cond* clause.
TRUE-EXPS is a list of Lisp expressions to be executed if this
condition is true, and inside its bindings.
UNCONDIT-CLAUSES is a list of cond*-clauses to be executed if this
condition is true, and inside its bindings.
This is used for non-exit clauses; it is nil for conditional-exit clauses.

REST and IFFALSE are non-nil for conditional-exit clauses that are not final.
REST is a list of clauses to process after this one if
this one could have exited but does not exit.
This is used for conditional exit clauses.
IFFALSE is the value to compute after this one if
this one could have exited but does not exit.
This is used for conditional exit clauses."
  (if (and uncondit-clauses rest)
      (error "Clase is both exiting and non-exiting-nil"))
  (let ((pat-type (car-safe condition)))
    (cond ((eq pat-type 'bind*)
           ;; When a bind* needs to be tested as a condition,
           ;; which is whenever that clause has elements after
           ;; the bind* element itself, the condition value
           ;; is the value of the last binding made.
           (let* ((lastbinding
                   ;; The last binding.
                   (car-safe (last condition)))
                  (last-value
                   ;; The initial value specified in the last binding.
                   (if (symbolp lastbinding) nil
                     (car-safe (cdr-safe lastbinding)))))
             (if rest
                 ;; bind* starts an exiting clause which is not final.
                 `(if ,last-value
                      (let* ,(cdr condition)
                        . ,true-exps)
                    ,iffalse)
               (if uncondit-clauses
                   ;; bind* starts a non-exit clause.
                   ;; Run the TRUE-EXPS.
                   ;; Then always go on to run the UNCONDIT-CLAUSES.
                   `(progn
                      (if ,last-value
                          (let* ,(cdr condition)
                            . ,true-exps))
                      (let* ,(cdr condition)
                        ,(cond*-convert uncondit-clauses)))
                 ;; bind* starts an exiting clause which is final.
                 ;; If there are TRUE-EXPS, run them if condition succeeded.
                 ;; Always make the bindings, in case the
                 ;; initial values have side effects.
                 `(if ,last-value
                      (let* ,(cdr condition)
                        . ,true-exps))))))
          ((eq pat-type 'match*)
           (cond*-match condition true-exps uncondit-clauses iffalse))
          (t
           ;; Ordinary Lixp expression is the condition 
           (if rest
               ;; A nonfinal exiting clause.
               ;; If condition succeeds, run the TRUE-EXPS.
               ;; There are following clauses, so run IFFALSE
               ;; if the condition fails.
               `(if ,condition
                    (progn . ,true-exps)
                  ,iffalse)
             (if uncondit-clauses
                 ;; A non-exit clause.
                 ;; If condition succeeds, run the TRUE-EXPS.
                 ;; Then always go on to run the UNCONDIT-CLAUSES.
                 `(progn (if ,condition
                             (progn . ,true-exps))
                         ,(cond*-convert uncondit-clauses))
               ;; An exiting clause which is also final.
               ;; If there are TRUE-EXPS, run them if CONDITION succeeds.
               (if true-exps
                   `(if ,condition (progn . ,true-exps))
                 ;; Run and return CONDITION.
                 condition)))))))
\f
(defun cond*-match (matchexp true-exps uncondit-clauses iffalse)
  "Generate code to match a match* pattern PATTERN.
Match it against data represented by the expression DATA.
TRUE-EXPS, UNCONDIT-CLAUSES and IFFALSE have the same meanings
as in `cond*-condition'."
  (when (or (null matchexp) (null (cdr-safe matchexp))
            (null (cdr-safe (cdr matchexp)))
            (cdr-safe (cdr (cdr matchexp))))
    (error "Malformed (match* ...) expression"))
  (let* (raw-result
         (pattern (nth 1 matchexp))
         (data (nth 2 matchexp))
         expression
         (inner-data data)
         ;; Add backtrack aliases for or-subpatterns to cdr of this.
         (backtrack-aliases (list nil))
         gensym)
    ;; For now, always bind a gensym to the data to be matched.
    (setq gensym (gensym "d") inner-data gensym)
    ;; Process the whole pattern as a subpattern.
    (setq raw-result (cond*-subpat pattern nil nil backtrack-aliases inner-data))
    (setq expression (cdr raw-result))
    ;; Run TRUE-EXPS if match succeeded.  Bind our bindings around it.
    (setq expression
          `(if ,expression
               ,(if (not (and backtrack-aliases (null uncondit-clauses)))
                    ;; Bind these here if there are no UNCONDIT-CLAUSES.
                    `(let ,(mapcar 'cdr (cdr backtrack-aliases))
                       (let* ,(car raw-result)
                        ,@true-exps))
                  `(let* ,(car raw-result)
                     ,@true-exps))
             ;; For a non-final exiting clause, run IFFALSE if match failed.
             ;; Don't bind the bindings for following clauses
             ;; since an exiting clause's bindings don't affect later clauses.
             ,iffalse))
    ;; For a non-final non-exiting clause,
    ;; always run the UNCONDIT-CLAUSES.
    (if uncondit-clauses
        (setq expression
              `(progn ,expression 
                      (let* ,(car raw-result)
                        ,(cond*-convert uncondit-clauses)))))
    ;; If there are backtrack aliases, bind them around the UNCONDIT-CLAUSES.
    (if (and backtrack-aliases uncondit-clauses)
      (setq expression `(let ,(mapcar 'cdr (cdr backtrack-aliases))
                          ,expression)))
    ;; If we used a gensym, add code to bind it.
    (if gensym
        `(let ((,gensym ,data)) ,expression)
      expression)))

(defun cond*-bind-around (bindings exp)
  "Wrap a `let*' around EXP, to bind those of BINDINGS used in EXP."
  `(let* ,(nreverse (cond*-used-within bindings exp)) ,exp))

(defun cond*-used-within (bindings exp)
  "Return the list of those bindings in BINDINGS which EXP refers to.
This operates naively and errs on the side of overinclusion,
and does not distinguish function names from variable names.
That is safe for the purpose this is used for."
  (cond ((symbolp exp) 
         (let ((which (assq exp bindings)))
           (if which (list which))))
        ((listp exp)
         (let (combined (rest exp))
           (while rest
             (let ((in-this-elt (cond*-used-within bindings (car rest))))
               (while in-this-elt
                 (unless (assq (car-safe in-this-elt) combined)
                   (push (car-safe in-this-elt) combined))
                 (pop in-this-elt)))
             (pop rest))
           combined))))

;;; ??? Structure type patterns not implemented yet.
;;; ??? Probably should optimize the `nth' calls in handling `list'.

(defun cond*-subpat (subpat cdr-safe bindings backtrack-aliases data)
  "Generate code to match ibe subpattern within `match*'.
SUBPAT is the subpattern to handle.
CDR-SAFE if true means don't verify there are no extra elts in a list.
BINDINGS is the list of bindings made by
the containing and previous subpatterns of this pattern.
Each element of BINDINGS must have the frm (VAR VALUE).
BACKTRACK-ALIASES is used to pass adta uward.  Initial call should
pass (list).  The cdr of this collects backtracking aliases made for
variables boung within (or...) patterns so that the caller
dna bind them etc.
DATA is the expression for the data that this subpattern is
supposed to match against.

Return Value has the form (BINDINGS . CONDITION), where
BINDINGS is the list of bindings to be made for SUBPAT
plus the subpatterns that contain/precede it.
Each element of BINDINGS has the form (VAR VALUE).
CONDITION is the condition to be tested to decide
whether SUBPAT (as well as the subpatterns that contain/precede it) matches,"
  (cond ((eq subpat '_)
         ;; _ as pattern makes no bindings and matches any data.
         (cons bindings t))
        ((symbolp subpat)
         ;; Bind or match a symbol to this data
         (let ((this-binding (assq subpat bindings)))
           (if this-binding
               ;; Variable already bound.
               ;; Compare what this variable should be bound to
               ;; to the fata it is supposed to match.
               ;; That is because we don't actually bind thes bindings
               ;; around the condition-testing expression.
               (cons bindings `(equal ,(cdr this-binding) ,data))
             ;; Inside or subpattern, if this symbol already has an alias
             ;; for backtracking, just use that.
             (let ((this-alias (assq subpat (cdr backtrack-aliases))))
               (if this-alias (cdr this-alias)
                 (if backtrack-aliases
                     ;; Inside or subpattern but this symbol has no alias,
                     ;; make one for it.
                     (progn (setcdr backtrack-aliases (cons (cons subpat (gensym "ba"))
                                                            (cdr backtrack-aliases)))
                            ;; Init the binding to symbol's backtrack-alias
                            ;; and set the alias to nil.
                            (cons `((,subpat ,(cdar (cdr backtrack-aliases))) . ,bindings)
                                  t                                  ))
                   (cons `((,subpat ,data) . ,bindings)
                         t)))))))
;;; This is not true any more.
;;;         ;; Actually we bind it to nil at the start of the clause
;;;         ;; and set it to the matched value if it matches.
;;;         (cons `((,subpat nil) . ,bindings)
;;;               `(progn (setq ,subpat ,data) t)))
        ;; Various constants.
        ((numberp subpat)
         (cons bindings `(eql ,subpat ,data)))
        ((keywordp subpat)
         (cons bindings `(eq ,subpat ,data)))
        ((memq subpat '(nil t))
         (cons bindings `(eq ,subpat ,data)))
        ;; Regular expressions as strings.
        ((stringp subpat)
         (cons bindings `(string-match ,(concat subpat "\\>") ,data)))
        ;; All other atoms match with `equal'.
        ((not (consp subpat))
         (cons bindings `(equal ,subpat ,data)))
        ((not (consp (cdr subpat)))
         (error "%s subpattern malformed or missing arguments" (car subpat)))
        ;; Regular expressions specified as list structure.
        ;; (rx REGEXP VARS...)
        ((eq (car subpat) 'rx)
         (let* ((rxpat (concat (rx-to-string (cadr subpat) t) "\\>"))
                (vars (cddr subpat)) setqs (varnum 0)
                (match-exp `(string-match ,rxpat ,data)))
           (if (null vars)
               (cons bindings match-exp)
             ;; There are variables to bind to the matched substrings.
             (if (> (length vars) 10)
                 (error "Too many variables specified for matched substrings"))
             (dolist (elt vars)
               (unless (symbolp elt)
                 (error "Non-symbol %s given as name for matched substring" elt)))
             ;; Bind these variables to nil, before the pattern.
             (setq bindings (nconc (mapcar 'list vars) bindings))
             ;; Make the expressions to set the variables.
             (setq setqs (mapcar
                          (lambda (var)
                            (prog1 `(setq ,var (match-string ,varnum ,data))
                              (setq varnum (1+ varnum))))
                          vars))
             (cons bindings `(if ,match-exp
                                 (progn ,@setqs t))))))
        ;; Quoted object as constant to match with `equal'.
        ((eq (car subpat) 'quote)
         (cons bindings `(equal ,subpat ,data)))
        ;; Match a call to `cons' by destructuring.
        ((eq (car subpat) 'cons)
         (let (car-result cdr-result car-exp cdr-exp)
           (setq car-result
                 (cond*-subpat (nth 1 subpat) cdr-safe bindings backtrack-aliases `(car ,data)))
           (setq bindings (car car-result)
                 car-exp (cdr car-result))
           (setq cdr-result
                 (cond*-subpat (nth 2 subpat) cdr-safe bindings backtrack-aliases `(cdr ,data)))
           (setq bindings (car cdr-result)
                 cdr-exp (cdr cdr-result))
           (cons bindings
                 `(and ,car-exp ,cdr-exp))))
        ;; Match a call to `list' by destructuring.
        ((eq (car subpat) 'list)
         (let ((i 0) expressions)
           ;; Check for bad structure of SUBPAT here?
           (dolist (this-elt (cdr subpat))
             (let ((result 
                    (cond*-subpat this-elt cdr-safe bindings backtrack-aliases `(nth ,i ,data))))
               (setq i (1+ i))
               (setq bindings (car result))
               (push (cdr result) expressions)))
           ;; Verify that list ends here, if we are suppose to check that.
           (unless cdr-safe
             (push `(null (nthcdr ,i ,data)) expressions))
           (cons bindings `(and . ,(nreverse expressions)))))
        ;; Match a call to `vector' by destructuring.
        ((eq (car subpat) 'vector)
         (let* ((vector (cadr subpat))
                (length (length vector)) expressions)
           (dotimes (i length)
             (let* ((result 
                     (cond*-subpat (aref i vector) cdr-safe
                                   bindings backtrack-aliases `(aref ,i ,data))))
               (setq bindings (car result))
               (push (cdr result) expressions)))
           (cons bindings `(and . ,(nreverse expressions)))))
        ;; Subpattern to set the cdr-safe flag
        ((eq (car subpat) 'cdr-safe)
         (cond*-subpat (cadr subpat) t bindings backtrack-aliases data))
        ;; Subpattern to clear the cdr-safe flag
        ((eq (car subpat) 'cdr)
         (cond*-subpat (cadr subpat) nil bindings backtrack-aliases data))
        ;; Handle conjunction subpatterns.
        ((eq (car subpat) 'and)
         (let (expressions)
           ;; Check for bad structure of SUBPAT here?
           (dolist (this-elt (cdr subpat))
             (let ((result 
                    (cond*-subpat this-elt cdr-safe bindings backtrack-aliases data)))
               (setq bindings (car result))
               (push (cdr result) expressions)))
           (cons bindings `(and . ,(nreverse expressions)))))
        ;; Handle disjunction subpatterns.
        ((eq (car subpat) 'or)
         ;; The main complexity is unsetting the pattern variables
         ;; that will not have matched.
         (let (expressions)
           ;; Check for bad structure of SUBPAT here?
           (dolist (this-elt (cdr subpat))
             (let* ((result 
                     (cond*-subpat this-elt cdr-safe bindings backtrack-aliases data))
                    (bindings-before-or bindings)
                    bindings-to-clear expression)
               (setq bindings (car result))
               (setq expression (cdr result))
               ;; Were any bindings made by this arm of the disjunction?
               (when (not (eq bindings bindings-before-or))
                 ;; Ok, arrange to clear their backtrack aliases
                 ;; if this arm does not match.
                 (setq bindings-to-clear bindings)
                 (let (clearing)
                   ;; For each of those bindings,
                   (while (not (eq bindings-to-clear bindings-before-or))
                     ;; Make an expression to set it to nil, in CLEARING.
                     (let* ((this-variable (caar bindings-to-clear))
                            (this-backtrack (assq this-variable
                                                  (cdr backtrack-aliases))))
                       (push `(setq ,(cdr this-backtrack) nil) clearing))
                     (setq bindings-to-clear (cdr bindings-to-clear)))
                   ;; Wrap EXPRESSION to clear those backtrack aliases
                   ;; if EXPRESSION is false.
                   (setq expression
                         (if (null clearing)
                             expression
                           (if (null (cdr clearing))
                               `(or ,expression
                                    ,(car clearing))
                             `(progn ,@clearing))))))
               (push expression expressions)))
           (cons bindings `(or . ,(nreverse expressions)))))
        ;; Expand cond*-macro call, treat result as a subpattern.
        ((get (car subpat) 'cond*-expander)
         ;; Treat result as a subpattern.
         (cond*-subpat (funcall (get (car subpat) 'cond*-expander) subpat)
                       cdr-safe bindings backtrack-aliases data))
        ((macrop (car subpat))
         (cond*-subpat (macroexpand subpat) cdr-safe bindings backtrack-aliases data))
        ;; Simple constrained variable, as in (symbolp x).
        ((functionp (car subpat))
         ;; Without this, nested constrained variables just work.
         (unless (symbolp (cadr subpat))
           (error "Complex pattern nested in constrained variable pattern"))
         (let* ((rest-args (cddr subpat))
                ;; Process VAR to get a binding for it.
                (result (cond*-subpat (cadr subpat) cdr-safe bindings backtrack-aliases data))
                (new-bindings (car result))
                (expression (cdr result))
                (combined-exp
                 `(and (,(car subpat) ,data . ,rest-args) ,expression)))
           (cons new-bindings
                 (cond*-bind-around new-bindings combined-exp))))
        ;; Generalized constrained variable: (constrain VAR EXP)
        ((eq (car subpat) 'constrain)
         ;; Without this, nested constrained variables just work.
         (unless (symbolp (cadr subpat))
           (error "Complex pattern nested in constrained variable pattern"))
         ;; Process VAR to get a binding for it.
         (let ((result (cond*-subpat (cadr subpat) cdr-safe bindings backtrack-aliases data)))
           (cons (car result)
                 ;; This is the test condition 
                 (cond*-bind-around (car result) (nth 2 subpat)))))
        (t (error "Undefined pattern type `%s' in `cond*'" (car subpat)))))

-- 
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] 134+ messages in thread

* Re: Code for cond*
  2024-01-19 10:42   ` João Távora
@ 2024-01-21  3:04     ` Richard Stallman
  2024-01-21 20:05       ` Adam Porter
  2024-01-24  9:48       ` Stefan Kangas
  0 siblings, 2 replies; 134+ messages in thread
From: Richard Stallman @ 2024-01-21  3:04 UTC (permalink / raw)
  To: João Távora; +Cc: acorallo, 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. ]]]

cond* has four basic advances over pcase: making bindings that cover
the rest of the body, matching patterns against various data objects
(not forcibly the same one), use of ordinary Lisp expressions as
conditions in clauses, and the abolity to make bindings and continue
with further clauses.

I'm going to do some more testing and then install cond*.

-- 
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] 134+ messages in thread

* Re: Code for cond*
  2024-01-21  3:04     ` Richard Stallman
@ 2024-01-21 20:05       ` Adam Porter
  2024-01-22  5:32         ` tomas
  2024-01-23 13:39         ` Richard Stallman
  2024-01-24  9:48       ` Stefan Kangas
  1 sibling, 2 replies; 134+ messages in thread
From: Adam Porter @ 2024-01-21 20:05 UTC (permalink / raw)
  To: rms; +Cc: acorallo, emacs-devel, joaotavora

Dear Richard,

With regard to these comments you made about Pcase and cond*:

> cond* has four basic advances over pcase: making bindings that cover
> the rest of the body, matching patterns against various data objects
> (not forcibly the same one), use of ordinary Lisp expressions as
> conditions in clauses, and the abolity to make bindings and continue
> with further clauses.

Respectfully, I'd like to point out a few things:

* "making bindings that cover the rest of the body...the ability to make 
bindings and continue with further clauses"

As several here have mentioned, this is not universally perceived as an 
advance.  It can easily lead to ambiguity and cause confusion.  It is 
not very "Lispy" (similarly to cl-loop, where the bounds of its bindings 
are ultimately contained at the top level, but can be introduced without 
explicit or obvious bounds).

* "matching patterns against various data objects (not forcibly the same 
one)"

Pcase can already do this (e.g. with its `guard' pattern).  As well, it 
destructures objects that cond* does not, such as maps, structs, and 
EIEIO objects.

* "use of ordinary Lisp expressions as conditions in clauses"

Pcase can already do this with its `guard' pattern, which explicitly 
demarcates an ordinary Lisp expression.

 > I'm going to do some more testing and then install cond*.

Obviously, this is your prerogative.  Yet, I would ask you again, 
respectfully, to reconsider.  Your stated reasons for writing cond* were 
various shortcomings of Pcase.  Some of those, e.g. the documentation, 
have already had volunteers step up to address.  The others could also 
be addressed in various ways.  I've suggested a few, but you haven't 
explained the reasons for rejecting them.

It's often suggested that one enhance this or that library in Emacs or 
ELPA rather than writing a new one that's similar but different.  This 
seems like one of those cases.  Why write a cond* expression, given its 
relative limitations, when one might need to rewrite it as Pcase later, 
given Pcase's other abilities?

As well, it's already a complaint by some that having to learn Pcase is 
a burden.  How will that burden be helped by having to learn both Pcase 
and cond*?

Thanks for your work on Emacs.

Sincerely,
Adam Porter



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

* Re: Code for cond*
  2024-01-21 20:05       ` Adam Porter
@ 2024-01-22  5:32         ` tomas
  2024-01-23 13:39         ` Richard Stallman
  1 sibling, 0 replies; 134+ messages in thread
From: tomas @ 2024-01-22  5:32 UTC (permalink / raw)
  To: emacs-devel

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

On Sun, Jan 21, 2024 at 02:05:24PM -0600, Adam Porter wrote:
> Dear Richard,

[...]

> * "making bindings that cover the rest of the body...the ability to make
> bindings and continue with further clauses"
> 
> As several here have mentioned, this is not universally perceived as an
> advance.  It can easily lead to ambiguity and cause confusion.  It is not
> very "Lispy" (similarly to cl-loop, where the bounds of its bindings are
> ultimately contained at the top level, but can be introduced without
> explicit or obvious bounds).

I agree on this one. This is the single feature which would confuse me
most: more and more I expect bindings to "stay whithin their lexical
context" with little exceptions. This would be one more.

Cheers
-- 
t

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]

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

* Re: Code for cond*
  2024-01-21 20:05       ` Adam Porter
  2024-01-22  5:32         ` tomas
@ 2024-01-23 13:39         ` Richard Stallman
  2024-01-24  6:02           ` Po Lu
  1 sibling, 1 reply; 134+ messages in thread
From: Richard Stallman @ 2024-01-23 13:39 UTC (permalink / raw)
  To: Adam Porter; +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. ]]]

  > * "making bindings that cover the rest of the body...the ability to make 
  > bindings and continue with further clauses"

  > As several here have mentioned, this is not universally perceived as an 
  > advance.

You're entitled to your opinion.  I consider this an important
advance, so I list it as one.

  > * "use of ordinary Lisp expressions as conditions in clauses"

  > Pcase can already do this with its `guard' pattern, which explicitly 
  > demarcates an ordinary Lisp expression.

Maybe you sare right about this one.

  > * "matching patterns against various data objects (not forcibly the same 
  > one)"

  > Pcase can already do this (e.g. with its `guard' pattern).

I don't see how `guard' does this job.  Can a guard pattern
destructure?  As I understand it, it contains only a Lisp expression.

                                                                As well, it 
  > destructures objects that cond* does not, such as maps, structs, and 
  > EIEIO objects.

I will implement the structure pattern when I get the necessary information
to do it.  I would like this to get done, but I can wait for you to tell
me what I need to know.

  > As well, it's already a complaint by some that having to learn Pcase is 
  > a burden.  How will that burden be helped by having to learn both Pcase 
  > and cond*?

By reducing the use of pcase, it will make code simpler to read.

If pcase lacked features for certain specific jobs, it would be esay
to fix that by adding a few features.  However, the problem with pcase
is that it has too many features for the job it does.  cond* does the
same jobs with fewer features because they work together better.

-- 
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] 134+ messages in thread

* Re: Code for cond*
  2024-01-18  3:37 Code for cond* Richard Stallman
  2024-01-18  4:59 ` Emanuel Berg
  2024-01-18 15:44 ` Andrea Corallo
@ 2024-01-23 18:10 ` Stefan Monnier via Emacs development discussions.
  2024-01-24  4:49   ` JD Smith
                     ` (2 more replies)
  2024-01-24 12:39 ` Alan Mackenzie
  3 siblings, 3 replies; 134+ messages in thread
From: Stefan Monnier via Emacs development discussions. @ 2024-01-23 18:10 UTC (permalink / raw)
  To: emacs-devel

> Here is the first draft of cond*.  I have tested some cases
> but I ask others to help in testing it more thoroughly.

AFAICT the main reason to oppose the use of `pcase` is that it increases
the size of the language, making it harder for non-experts to
read/understand/modify ELisp code.

`cond*` suffers from the same criticism.  And having both even more so
(and whether you like it or not, `pcase` is probably not going to
disappear any time soon, among other things because it offers a style
familiar from other languages and that style is gaining popularity).

So, I'm not super enthusiastic about adding such a new form, but being
responsible for the introduction of several such new constructs in ELisp
over the years, I do feel a bit like the pot calling the kettle black.

So, rather than oppose it, I'll just point out some things which I think
could be improved (or which offend my taste, as the case may be).

>   (cond* 
>        ;; Same as a clause in `cond',
>        (CONDITION
>         DO-THIS-IF-TRUE-THEN-EXIT...)

So far so good.

>        ;; Execute FORM, and ignore its value
>        ;; (except if this is the last clause).
>        (FORM)

YAGNI.

>        ;; Variables to bind, as in let*, around the rest
>        ;; of the cond*.
>        ((bind* (x foobar) y z (foo 5) a))
>        ;; Bindings continue in effect for the whole cond* construct.

This is one of the functionality missing from `cond`, so I'm rather
favorable, but:
- why `bind*` when the rest of ELisp uses `let*` in the names of all
  related constructs?
- Why the double parens?
- Why `*`?  If you need such sequential bindings, you can get it with

    (cond
     [...]
     ((bind* (x E1)))
     ((bind* (y (foo x))))

  So here a plain old "parallel let" would be more useful (or restrict
  that binding to a single var, so the question doesn't even pop up).

>        ;; Bind variables for the clause, as in let*.
>        ((bind* (foo 5) (condition (hack-it foo)))
>         ;; condition is that the last variable bound be non-nil.
>         BODY...)

This is the other of the functionality missing from `cond`, so I'm
rather favorable, but:
- why `bind*` when the rest of ELisp uses "let*" in the names of all
  related constructs?
- I'm not happy about using the same construct for "bindings
  for this clause" and "bindings for subsequent clauses".
Maybe it should be inspired by `when-let*`?

>        ;; Extracts substructure and binds variables for the clause.
>        ((match* `(expt ,foo ,bar) x)
>         DO-THIS-IF-IT-MATCHED-THEN-EXIT...)
>        ;; Bindings no longer in effect.

This `(expt ,foo ,bar) is a valid Pcase pattern, so it would be odd if
other Pcase patterns can't be used here.
For that same reason, it's odd for its name not to include "pcase".

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

Finally, I see a fairly extensive but hardcoded pattern language, which
seems like a regression compared to Pcase where the pattern language is
built from a few primitives only (with the rest defined on top via
`pcase-defmacro`).  The worst part, tho, is that the two pattern
languages are very similar, and I can't see any good reason for
the differences.  It feels like an NIH reaction.


        Stefan




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

* Re: Code for cond*
  2024-01-23 18:10 ` Stefan Monnier via Emacs development discussions.
@ 2024-01-24  4:49   ` JD Smith
  2024-01-24  9:45     ` Stefan Kangas
                       ` (2 more replies)
  2024-01-26  4:30   ` Richard Stallman
  2024-01-26  4:30   ` Richard Stallman
  2 siblings, 3 replies; 134+ messages in thread
From: JD Smith @ 2024-01-24  4:49 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel


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




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

* Re: Code for cond*
  2024-01-23 13:39         ` Richard Stallman
@ 2024-01-24  6:02           ` Po Lu
  0 siblings, 0 replies; 134+ messages in thread
From: Po Lu @ 2024-01-24  6:02 UTC (permalink / raw)
  To: Richard Stallman; +Cc: Adam Porter, emacs-devel

Richard Stallman <rms@gnu.org> writes:

> You're entitled to your opinion.  I consider this an important
> advance, so I list it as one.

And for what it's worth, you're not alone.



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

* Re: Code for cond*
  2024-01-24  4:49   ` JD Smith
@ 2024-01-24  9:45     ` Stefan Kangas
  2024-01-24 15:29       ` JD Smith
  2024-01-24 16:30     ` Drew Adams
  2024-01-25  3:16     ` Madhu
  2 siblings, 1 reply; 134+ messages in thread
From: Stefan Kangas @ 2024-01-24  9:45 UTC (permalink / raw)
  To: JD Smith, Stefan Monnier; +Cc: emacs-devel

JD Smith <jdtsmith@gmail.com> writes:

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

Patches welcome.



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

* Re: Code for cond*
  2024-01-21  3:04     ` Richard Stallman
  2024-01-21 20:05       ` Adam Porter
@ 2024-01-24  9:48       ` Stefan Kangas
  2024-01-24 10:09         ` Emanuel Berg
                           ` (3 more replies)
  1 sibling, 4 replies; 134+ messages in thread
From: Stefan Kangas @ 2024-01-24  9:48 UTC (permalink / raw)
  To: rms, joaotavora; +Cc: acorallo, emacs-devel, monnier, eliz

Richard Stallman <rms@gnu.org> writes:

> I'm going to do some more testing and then install cond*.

I'm sorry for paying so little attention to this.  I was not aware that
there was a plan to install `cond*', or I would have spoken up sooner.

While I don't want to claim that `pcase' is perfect and impossible to
improve upon, I don't find `cond*' convincing.

Whatever else is true, a new macro will increase the complexity of ELisp
and make the task of maintaining Emacs harder.  If the downsides to
having two ways of doing the same thing are to be worth it, it needs to
be demonstrated that the new one offers substantial improvements.

Here, it is not clear that `cond*' offers much in the way of either
simplifications, or powerful new ways to express ideas and solve
problems.  What I see instead is a mere _version_ of `pcase'.

Now, the `pcase' macro has been with us for 14 years already, and we
have had plenty of time to learn its ins and outs.  It's heavily used in
many parts of Emacs, and many of us rely on it as a matter of routine.
Its success is not surprising, given that this style of pattern matching
is increasingly common in other programming languages.

Does the "install `cond*'" proposal come with a plan to replace `pcase'
everywhere?  That sounds like a bad idea: it would lead to code churn,
unhappy maintainers of subsystems that use `pcase', and, of course, bugs
in both the code being changed and in `cond*' itself.

If it does _not_ come with such a plan, it's slightly better.  But then
that begs the question: why add it?

To avoid having to bikeshed about whether or not to use `pcase' or
`cond*' every single time, I think we will have to capitulate the
argument, as we sometimes do, and say that the choice is a matter of
personal style.  Which, if we are being really honest, it really is.

We will end up with `pcase' for those that happen to prefer that, and
`cond*' for those that happen to prefer that.  For me, as a maintainer,
I now have to know not one, but two relatively complex macros.  While I
realize that ELisp has been largely designed in an ad-hoc way,
consciously introducing such needless proliferation does not strike me
as a particularly good way to design a programming system.

In summary, I recommend installing `cond*' as a new package on GNU ELPA.
This is a good way of exploring an alternative version of an existing
macro.



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

* Re: Code for cond*
  2024-01-24  9:48       ` Stefan Kangas
@ 2024-01-24 10:09         ` Emanuel Berg
  2024-01-24 11:30         ` João Távora
                           ` (2 subsequent siblings)
  3 siblings, 0 replies; 134+ messages in thread
From: Emanuel Berg @ 2024-01-24 10:09 UTC (permalink / raw)
  To: emacs-devel

Stefan Kangas wrote:

> If it does _not_ come with such a plan, it's slightly
> better. But then that begs the question: why add it?

We add it so we can move on, everyone is happy to have their
own thing, be it "cond*", `cl-lib', `pcase' or whatever, and
then everyone can move on and let this issue rest.

I'm not saying that makes sense necessarily from all angles of
the problem, just saying that would be the positive effect it
would have.

If people help with feedback to polish cond* to perfection I'm
sure it is an interesting piece of software.

Let's do that first, maybe?

-- 
underground experts united
https://dataswamp.org/~incal




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

* Re: Code for cond*
  2024-01-24  9:48       ` Stefan Kangas
  2024-01-24 10:09         ` Emanuel Berg
@ 2024-01-24 11:30         ` João Távora
  2024-01-24 12:08           ` João Távora
  2024-01-24 12:09         ` Po Lu
  2024-01-24 12:15         ` Alan Mackenzie
  3 siblings, 1 reply; 134+ messages in thread
From: João Távora @ 2024-01-24 11:30 UTC (permalink / raw)
  To: Stefan Kangas; +Cc: rms, acorallo, emacs-devel, monnier, eliz

On Wed, Jan 24, 2024 at 9:48 AM Stefan Kangas <stefankangas@gmail.com> wrote:

> We will end up with `pcase' for those that happen to prefer that, and
> `cond*' for those that happen to prefer that.  For me, as a maintainer,
> I now have to know not one, but two relatively complex macros.  While I
> realize that ELisp has been largely designed in an ad-hoc way,
> consciously introducing such needless proliferation does not strike me
> as a particularly good way to design a programming system.

+1 to everything said.  And if cond* does show up somewhere, please
really implementing its interface in terms of pcase, whenever possible
(it seems it is possible in most if not all cases).

> In summary, I recommend installing `cond*' as a new package on GNU ELPA.
> This is a good way of exploring an alternative version of an existing
> macro.

+1 here, too.

João



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

* Re: Code for cond*
  2024-01-24 11:30         ` João Távora
@ 2024-01-24 12:08           ` João Távora
  0 siblings, 0 replies; 134+ messages in thread
From: João Távora @ 2024-01-24 12:08 UTC (permalink / raw)
  To: Stefan Kangas; +Cc: rms, acorallo, emacs-devel, monnier, eliz

> > consciously introducing such needless proliferation does not strike me
> > as a particularly good way to design a programming system.
>
> +1 to everything said.  And if cond* does show up somewhere, please
> really implementing its interface in terms of pcase, whenever possible

       ^^^
     consider



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

* Re: Code for cond*
  2024-01-24  9:48       ` Stefan Kangas
  2024-01-24 10:09         ` Emanuel Berg
  2024-01-24 11:30         ` João Távora
@ 2024-01-24 12:09         ` Po Lu
  2024-01-24 12:22           ` Ihor Radchenko
  2024-01-24 12:15         ` Alan Mackenzie
  3 siblings, 1 reply; 134+ messages in thread
From: Po Lu @ 2024-01-24 12:09 UTC (permalink / raw)
  To: Stefan Kangas; +Cc: rms, joaotavora, acorallo, emacs-devel, monnier, eliz

Stefan Kangas <stefankangas@gmail.com> writes:

> If it does _not_ come with such a plan, it's slightly better.  But then
> that begs the question: why add it?

Because it will forever free us of such forms as:

  (let (x y z)
    (cond ((eq (setq x ...) 'bar)
           yyy)
          ((setq y (... x))
           zzz)
          ...and so on

This alone is too important a feature to be consigned to ELPA.



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

* Re: Code for cond*
  2024-01-24  9:48       ` Stefan Kangas
                           ` (2 preceding siblings ...)
  2024-01-24 12:09         ` Po Lu
@ 2024-01-24 12:15         ` Alan Mackenzie
  2024-01-24 12:28           ` Emanuel Berg
                             ` (2 more replies)
  3 siblings, 3 replies; 134+ messages in thread
From: Alan Mackenzie @ 2024-01-24 12:15 UTC (permalink / raw)
  To: Stefan Kangas; +Cc: rms, joaotavora, acorallo, emacs-devel, monnier, eliz

Hello, Stefan.

On Wed, Jan 24, 2024 at 01:48:53 -0800, Stefan Kangas wrote:
> Richard Stallman <rms@gnu.org> writes:

> > I'm going to do some more testing and then install cond*.

> I'm sorry for paying so little attention to this.  I was not aware that
> there was a plan to install `cond*', or I would have spoken up sooner.

> While I don't want to claim that `pcase' is perfect and impossible to
> improve upon, I don't find `cond*' convincing.

Richard's point, and I agree with it, is that pcase is so far from
perfect that it needs replacing.

> Whatever else is true, a new macro will increase the complexity of
> ELisp and make the task of maintaining Emacs harder.  If the downsides
> to having two ways of doing the same thing are to be worth it, it
> needs to be demonstrated that the new one offers substantial
> improvements.

> Here, it is not clear that `cond*' offers much in the way of either
> simplifications, or powerful new ways to express ideas and solve
> problems.  What I see instead is a mere _version_ of `pcase'.

It is not a version of pcase.  It is more like what pcase should have
been in the first place.

> Now, the `pcase' macro has been with us for 14 years already, and we
> have had plenty of time to learn its ins and outs.  It's heavily used in
> many parts of Emacs, and many of us rely on it as a matter of routine.
> Its success is not surprising, given that this style of pattern matching
> is increasingly common in other programming languages.

What is its "success"?  It is a failure, being accepted only by some
Emacs hackers.  It is difficult to learn, it may be easy to write, but
is difficult to read, and difficult indeed to debug.

> Does the "install `cond*'" proposal come with a plan to replace `pcase'
> everywhere?  That sounds like a bad idea: it would lead to code churn,
> unhappy maintainers of subsystems that use `pcase', and, of course, bugs
> in both the code being changed and in `cond*' itself.

I think this is the plan, yes.  We can replace uses of pcase with
equivalent cond* uses, thus improving the readability and
maintainability of our code at a stroke.

> If it does _not_ come with such a plan, it's slightly better.  But then
> that begs the question: why add it?

To solve the problems that pcase has.

> To avoid having to bikeshed about whether or not to use `pcase' or
> `cond*' every single time, I think we will have to capitulate the
> argument, as we sometimes do, and say that the choice is a matter of
> personal style.  Which, if we are being really honest, it really is.

When pcase was introduced, it was quickly and systematically forced into
(?)every bit of code it would conceivably fit into.  Something similar
could be done for cond*.

> We will end up with `pcase' for those that happen to prefer that, and
> `cond*' for those that happen to prefer that.  For me, as a maintainer,
> I now have to know not one, but two relatively complex macros.  While I
> realize that ELisp has been largely designed in an ad-hoc way,
> consciously introducing such needless proliferation does not strike me
> as a particularly good way to design a programming system.

cond* was designed with the benefit of experience gained from pcase, and
also after extensive open discussion.  pcase had neither of these.  It
is highly likely that cond* will be considerably better than pcase.

It is a bit like programming languages.  The Lisp we use in Emacs now is
quite a bit different from the original Lisp of 1957.  I'm sure we all
agree this is a good thing.

> In summary, I recommend installing `cond*' as a new package on GNU ELPA.
> This is a good way of exploring an alternative version of an existing
> macro.

That's just a way of ensuring it never comes to anything and just gets
forgotten about.  If that is done, it will be impossible to replace any
of the pcase uses in Emacs with cond*.

A better way of doing this would be to create a feature branch, which
can later be merged into master once its advantages become clear.

-- 
Alan Mackenzie (Nuremberg, Germany).



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

* Re: Code for cond*
  2024-01-24 12:09         ` Po Lu
@ 2024-01-24 12:22           ` Ihor Radchenko
  2024-01-24 12:33             ` Po Lu
  0 siblings, 1 reply; 134+ messages in thread
From: Ihor Radchenko @ 2024-01-24 12:22 UTC (permalink / raw)
  To: Po Lu; +Cc: Stefan Kangas, rms, joaotavora, acorallo, emacs-devel, monnier,
	eliz

Po Lu <luangruo@yahoo.com> writes:

> Stefan Kangas <stefankangas@gmail.com> writes:
>
>> If it does _not_ come with such a plan, it's slightly better.  But then
>> that begs the question: why add it?
>
> Because it will forever free us of such forms as:
>
>   (let (x y z)
>     (cond ((eq (setq x ...) 'bar)
>            yyy)
>           ((setq y (... x))
>            zzz)
>           ...and so on
>
> This alone is too important a feature to be consigned to ELPA.

May you elaborate how cond* helps simplifying the above example?

-- 
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>



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

* Re: Code for cond*
  2024-01-24 12:15         ` Alan Mackenzie
@ 2024-01-24 12:28           ` Emanuel Berg
  2024-01-25  9:10           ` Po Lu
  2024-01-25 23:32           ` Stefan Kangas
  2 siblings, 0 replies; 134+ messages in thread
From: Emanuel Berg @ 2024-01-24 12:28 UTC (permalink / raw)
  To: emacs-devel

Alan Mackenzie wrote:

>> While I don't want to claim that `pcase' is perfect and
>> impossible to improve upon, I don't find
>> `cond*' convincing.
>
> Richard's point, and I agree with it, is that pcase is so
> far from perfect that it needs replacing.

Okay, I thought you guys had peaceful co-existence in mind.

If "cond*" is actually intended to push out and replace
`pcase', then that idea is just bizarre and I agree with
almost everyone else saying one shouldn't add "cond*".

Sorry, put it in GNU ELPA. I have a package there so I know it
is a very nice place actually.

-- 
underground experts united
https://dataswamp.org/~incal




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

* Re: Code for cond*
  2024-01-24 12:22           ` Ihor Radchenko
@ 2024-01-24 12:33             ` Po Lu
  2024-01-24 13:34               ` Ihor Radchenko
  0 siblings, 1 reply; 134+ messages in thread
From: Po Lu @ 2024-01-24 12:33 UTC (permalink / raw)
  To: Ihor Radchenko
  Cc: Stefan Kangas, rms, joaotavora, acorallo, emacs-devel, monnier,
	eliz

Ihor Radchenko <yantar92@posteo.net> writes:

> Po Lu <luangruo@yahoo.com> writes:
>
>> Stefan Kangas <stefankangas@gmail.com> writes:
>>
>>> If it does _not_ come with such a plan, it's slightly better.  But then
>>> that begs the question: why add it?
>>
>> Because it will forever free us of such forms as:
>>
>>   (let (x y z)
>>     (cond ((eq (setq x ...) 'bar)
>>            yyy)
>>           ((setq y (... x))
>>            zzz)
>>           ...and so on
>>
>> This alone is too important a feature to be consigned to ELPA.
>
> May you elaborate how cond* helps simplifying the above example?

It will be possible for a clause to bind variables accessible in
subsequent ones, eliminating the let and setq forms.



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

* Re: Code for cond*
  2024-01-20  3:39   ` Richard Stallman
@ 2024-01-24 12:37     ` Po Lu
  2024-01-24 19:12     ` Alan Mackenzie
  1 sibling, 0 replies; 134+ messages in thread
From: Po Lu @ 2024-01-24 12:37 UTC (permalink / raw)
  To: Richard Stallman; +Cc: Emanuel Berg, emacs-devel

Richard Stallman <rms@gnu.org> writes:

>            ;; Ordinary Lixp expression is the condition 
                         ^^^^^^^^^^^^^^^

Typo, btw.  This should be "Lisp expression".



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

* Re: Code for cond*
  2024-01-18  3:37 Code for cond* Richard Stallman
                   ` (2 preceding siblings ...)
  2024-01-23 18:10 ` Stefan Monnier via Emacs development discussions.
@ 2024-01-24 12:39 ` Alan Mackenzie
  2024-01-24 14:43   ` Emanuel Berg
                     ` (2 more replies)
  3 siblings, 3 replies; 134+ messages in thread
From: Alan Mackenzie @ 2024-01-24 12:39 UTC (permalink / raw)
  To: Richard Stallman; +Cc: emacs-devel

Hello, Richard.

On Wed, Jan 17, 2024 at 22:37:47 -0500, Richard Stallman wrote:
> [[[ 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. ]]]

> Here is the first draft of cond*.  I have tested some cases
> but I ask others to help in testing it more thoroughly.

> I invite constructive comments, bug reports, patches,
> and suggestions.

[ .... ]

One thing I noticed was that the implementation makes no use of macros,
except for a trivial macro which "renames" cond* itself.

This is in contrase to pcase.el, which uses macros extensively,
requiring lisp/emacs-lisp/macroexp.el to work.  In its turn macroexp.el
uses pcase.  This leads to an unlovely artifice at bootstrap time.

The macro in cond* can be expanded by Fmacroexpand in src/eval.c.  Thus,
at bootstrap, condstar.el could simply be loaded before macroexpand.el.

This is a Good Thing, and I'm sure it's not an accident.  Please keep
this feature as cond* get amended and further developed.

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

-- 
Alan Mackenzie (Nuremberg, Germany).



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

* Re: Code for cond*
  2024-01-24 12:33             ` Po Lu
@ 2024-01-24 13:34               ` Ihor Radchenko
  2024-01-24 13:52                 ` João Távora
  2024-01-24 14:07                 ` Po Lu
  0 siblings, 2 replies; 134+ messages in thread
From: Ihor Radchenko @ 2024-01-24 13:34 UTC (permalink / raw)
  To: Po Lu; +Cc: Stefan Kangas, rms, joaotavora, acorallo, emacs-devel, monnier,
	eliz

Po Lu <luangruo@yahoo.com> writes:

>>>   (let (x y z)
>>>     (cond ((eq (setq x ...) 'bar)
>>>            yyy)
>>>           ((setq y (... x))
>>>            zzz)
>>>           ...and so on
>>>
>>> This alone is too important a feature to be consigned to ELPA.
>>
>> May you elaborate how cond* helps simplifying the above example?
>
> It will be possible for a clause to bind variables accessible in
> subsequent ones, eliminating the let and setq forms.

The problem is that cond* is doing much more than just introducing
fall-through bindings.

If we just need fall-through bindings, I'd argue that we should better
introduce them separately; maybe even as a part of the normal `cond'.

-- 
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>



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

* Re: Code for cond*
  2024-01-24 13:34               ` Ihor Radchenko
@ 2024-01-24 13:52                 ` João Távora
  2024-01-24 14:31                   ` Po Lu
                                     ` (2 more replies)
  2024-01-24 14:07                 ` Po Lu
  1 sibling, 3 replies; 134+ messages in thread
From: João Távora @ 2024-01-24 13:52 UTC (permalink / raw)
  To: Ihor Radchenko
  Cc: Po Lu, Stefan Kangas, rms, acorallo, emacs-devel, monnier, eliz

On Wed, Jan 24, 2024 at 1:30 PM Ihor Radchenko <yantar92@posteo.net> wrote:
>
> Po Lu <luangruo@yahoo.com> writes:
>
> >>>   (let (x y z)
> >>>     (cond ((eq (setq x ...) 'bar)
> >>>            yyy)
> >>>           ((setq y (... x))
> >>>            zzz)
> >>>           ...and so on
> >>>
> >>> This alone is too important a feature to be consigned to ELPA.
> >>
> >> May you elaborate how cond* helps simplifying the above example?
> >
> > It will be possible for a clause to bind variables accessible in
> > subsequent ones, eliminating the let and setq forms.
>
> The problem is that cond* is doing much more than just introducing
> fall-through bindings.
>
> If we just need fall-through bindings, I'd argue that we should better
> introduce them separately; maybe even as a part of the normal `cond'.

Aren't fallthrough bindings just a code smell, anyway? I mean, I've
used that 'setq' pattern in the past, but my intention is _never_
to get fallthrough, but to delay a potentially expensive
calculation to  just the point where it becomes useful for a
single clause.

I could easily use pcase for that, and the bindings would
be local to that clause.  If I need more clauses, I should
do a sub-cond, or sub-pcase.  That is the way IMHO.

Code-smell or not, it is nevertheless bizarre or at least
unhealthy -- multiple people have pointed that out --
that the new cond* fall-through bindings don't lexically wrap
their users.

João



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

* Re: Code for cond*
  2024-01-24 13:34               ` Ihor Radchenko
  2024-01-24 13:52                 ` João Távora
@ 2024-01-24 14:07                 ` Po Lu
  2024-01-24 14:20                   ` Ihor Radchenko
  1 sibling, 1 reply; 134+ messages in thread
From: Po Lu @ 2024-01-24 14:07 UTC (permalink / raw)
  To: Ihor Radchenko
  Cc: Stefan Kangas, rms, joaotavora, acorallo, emacs-devel, monnier,
	eliz

Ihor Radchenko <yantar92@posteo.net> writes:

> The problem is that cond* is doing much more than just introducing
> fall-through bindings.

People appear to appreciate them, and as they say, "two is better than
one..."

Anyway, it is now your turn not to detract from features other people
want, just as it was ours during the height of the arguments against
pcase and cl-lib.  To install or not to install is Richard's decision to
make, and trying to prepossess him against his own invention strikes me
as petty spite, even though it is not meant to be.  I take no exception
to your (plural) enumerating long lists of reasons why you dislike
cond*, but presenting your perspective to the exclusion of alternative
viewpoints is misleading, and asking that Richard consign to ELPA on no
better grounds than your disapproval a <1000 LOC file defining a single
fundamental language construct designed for extensive use is none too
reasonable.  "Live and let live" goes both ways, not just the way of the
party opposed to features you prefer.

Thanks.



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

* Re: Code for cond*
  2024-01-24 14:07                 ` Po Lu
@ 2024-01-24 14:20                   ` Ihor Radchenko
  2024-01-24 14:34                     ` Po Lu
  0 siblings, 1 reply; 134+ messages in thread
From: Ihor Radchenko @ 2024-01-24 14:20 UTC (permalink / raw)
  To: Po Lu; +Cc: Stefan Kangas, rms, joaotavora, acorallo, emacs-devel, monnier,
	eliz

Po Lu <luangruo@yahoo.com> writes:

> Ihor Radchenko <yantar92@posteo.net> writes:
>
>> The problem is that cond* is doing much more than just introducing
>> fall-through bindings.
>
> People appear to appreciate them, and as they say, "two is better than
> one..."
>
> Anyway, ...

FYI, I cannot understand the argument you are making.

I was replying to the following argument
>>> This alone is too important a feature to be consigned to ELPA.

My point is that "too important feature" does not have to be linked to
the proposed cond* construct. If we really think that fall-through
bindings are very important, installing cond* is not the only possible
way to introduce fall-through bindings into Elisp.

So, regardless of the outcome of this discussion, we may consider
fall-through bindings separately, and maybe add them to more
conventional Elisp constructs like ordinary `cond'.

-- 
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>



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

* Re: Code for cond*
  2024-01-24 13:52                 ` João Távora
@ 2024-01-24 14:31                   ` Po Lu
  2024-01-27  3:35                   ` Richard Stallman
  2024-01-27  3:35                   ` Richard Stallman
  2 siblings, 0 replies; 134+ messages in thread
From: Po Lu @ 2024-01-24 14:31 UTC (permalink / raw)
  To: João Távora
  Cc: Ihor Radchenko, Stefan Kangas, rms, acorallo, emacs-devel,
	monnier, eliz

João Távora <joaotavora@gmail.com> writes:

> Aren't fallthrough bindings just a code smell, anyway? I mean, I've
> used that 'setq' pattern in the past, but my intention is _never_ to
> get fallthrough, but to delay a potentially expensive calculation to
> just the point where it becomes useful for a single clause.
>
> I could easily use pcase for that, and the bindings would be local to
> that clause.  If I need more clauses, I should do a sub-cond, or
> sub-pcase.  That is the way IMHO.
>
> Code-smell or not, it is nevertheless bizarre or at least unhealthy --
> multiple people have pointed that out -- that the new cond*
> fall-through bindings don't lexically wrap their users.

By your own admission, that is one of many possible honest opinions.

Mine is that every further cond form within a cond clause increases
indentation by several columns, and it is not long before both the
indentation and the nesting hinders understanding the forms concerned.
People who share this opinion have and will instinctively reach for let
and setq--unless a better alternative emerges, which this is.

Perhaps last month's scrum went over the line, and passions on both
sides were inflamed, after a fashion.  But what's the meaning of so
aggressively stonewalling a new feature, which is being introduced
unattended by any proposal to remove or replace pcase or
destructing-bind or similar?



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

* Re: Code for cond*
  2024-01-24 14:20                   ` Ihor Radchenko
@ 2024-01-24 14:34                     ` Po Lu
  2024-01-24 14:44                       ` Ihor Radchenko
  0 siblings, 1 reply; 134+ messages in thread
From: Po Lu @ 2024-01-24 14:34 UTC (permalink / raw)
  To: Ihor Radchenko
  Cc: Stefan Kangas, rms, joaotavora, acorallo, emacs-devel, monnier,
	eliz

Ihor Radchenko <yantar92@posteo.net> writes:

> FYI, I cannot understand the argument you are making.

It was not addressed specifically to you.  My point is that the same
crowd who were defending cl-lib and pcase as though their life was at
stake are now pushing for the exclusion of an alternative, which
everyone can see the irony in.



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

* Re: Code for cond*
  2024-01-24 12:39 ` Alan Mackenzie
@ 2024-01-24 14:43   ` Emanuel Berg
  2024-01-24 16:25   ` Manuel Giraud via Emacs development discussions.
  2024-01-25 14:01   ` Code for cond* - cond*-match, cond*-subpat and backtrack-aliases Alan Mackenzie
  2 siblings, 0 replies; 134+ messages in thread
From: Emanuel Berg @ 2024-01-24 14:43 UTC (permalink / raw)
  To: emacs-devel

Alan Mackenzie wrote:

> One thing I noticed was that the implementation makes no use
> of macros, except for a trivial macro which "renames"
> cond* itself.
>
> This is in contrase to pcase.el, which uses macros
> extensively,

Oh, no, not this campaign again!

Guys this is crazy, this must be stopped as soon as possible.

People are not throwing away any of our good software, they
aren't gonna take it ever. No means no so forget about it.

This campaign will only create conflicts where people feel they
must engage in discussions defending claims and ideas that are
just astonishing to them.

People want to do normal and fun things with Emacs, not be
part of politics and division of some irrational conflict or
power-struggle in software.

-- 
underground experts united
https://dataswamp.org/~incal




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

* Re: Code for cond*
  2024-01-24 14:34                     ` Po Lu
@ 2024-01-24 14:44                       ` Ihor Radchenko
  2024-01-24 14:47                         ` João Távora
  2024-01-24 15:28                         ` Emanuel Berg
  0 siblings, 2 replies; 134+ messages in thread
From: Ihor Radchenko @ 2024-01-24 14:44 UTC (permalink / raw)
  To: Po Lu; +Cc: Stefan Kangas, rms, joaotavora, acorallo, emacs-devel, monnier,
	eliz

Po Lu <luangruo@yahoo.com> writes:

> Ihor Radchenko <yantar92@posteo.net> writes:
>
>> FYI, I cannot understand the argument you are making.
>
> It was not addressed specifically to you.  My point is that the same
> crowd who were defending cl-lib and pcase as though their life was at
> stake are now pushing for the exclusion of an alternative, which
> everyone can see the irony in.

I am not sure if pointing this out is constructive. Coming from the same
crowd or not, the useful arguments should be considered.

-- 
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>



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

* Re: Code for cond*
  2024-01-24 14:44                       ` Ihor Radchenko
@ 2024-01-24 14:47                         ` João Távora
  2024-01-24 15:28                         ` Emanuel Berg
  1 sibling, 0 replies; 134+ messages in thread
From: João Távora @ 2024-01-24 14:47 UTC (permalink / raw)
  To: Ihor Radchenko
  Cc: Po Lu, Stefan Kangas, rms, acorallo, emacs-devel, monnier, eliz

On Wed, Jan 24, 2024 at 2:41 PM Ihor Radchenko <yantar92@posteo.net> wrote:
>
> Po Lu <luangruo@yahoo.com> writes:
>
> > Ihor Radchenko <yantar92@posteo.net> writes:
> >
> >> FYI, I cannot understand the argument you are making.
> >
> > It was not addressed specifically to you.  My point is that the same
> > crowd who were defending cl-lib and pcase as though their life was at
> > stake are now pushing for the exclusion of an alternative, which
> > everyone can see the irony in.
>
> I am not sure if pointing this out is constructive. Coming from the same
> crowd or not, the useful arguments should be considered.


+1.  Also, it's incredible how that "irony" doesn't apply to the
"crowd" complaining that Elisp is too complex introducing yet another
complex way to do much the same.

João



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

* Re: Code for cond*
  2024-01-24 14:44                       ` Ihor Radchenko
  2024-01-24 14:47                         ` João Távora
@ 2024-01-24 15:28                         ` Emanuel Berg
  2024-01-24 16:30                           ` Ihor Radchenko
  2024-01-25  9:06                           ` Po Lu
  1 sibling, 2 replies; 134+ messages in thread
From: Emanuel Berg @ 2024-01-24 15:28 UTC (permalink / raw)
  To: emacs-devel

Ihor Radchenko wrote:

>>> FYI, I cannot understand the argument you are making.
>>
>> It was not addressed specifically to you. My point is that
>> the same crowd who were defending cl-lib and pcase as
>> though their life was at stake are now pushing for the
>> exclusion of an alternative, which everyone can see the
>> irony in.
>
> I am not sure if pointing this out is constructive.
> Coming from the same crowd or not, the useful arguments
> should be considered.

Yes but we heard from you that the goal was to replace
`pcase'. It was the view of two people. The third is not here,
he is staying low?

If you say it right out, that is your agenda, so if the goal
is the replace pcase, we see the install of cond* as a first
step on that plan.

-- 
underground experts united
https://dataswamp.org/~incal




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

* Re: Code for cond*
  2024-01-24  9:45     ` Stefan Kangas
@ 2024-01-24 15:29       ` JD Smith
  2024-01-24 15:55         ` Stefan Monnier
  0 siblings, 1 reply; 134+ messages in thread
From: JD Smith @ 2024-01-24 15:29 UTC (permalink / raw)
  To: Stefan Kangas; +Cc: Stefan Monnier, emacs-devel


> On Jan 24, 2024, at 4:45 AM, Stefan Kangas <stefankangas@gmail.com> wrote:
> 
> JD Smith <jdtsmith@gmail.com> writes:
> 
>> But oddly enough, this thread discussing its potential replacement has
>> given me the key insight — “imagine running list interpolation
>> backwards”.  …  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.
> 
> Patches welcome.

To explain this correctly, I’d need to understand where the mental model breaks down.  For example, I was surprised that this forward/reverse symmetry was not maintained:

(let ((list-var '(foo moo loo))
      (other 'boo))
  `(,@list-var . ,other)). ; -> (foo moo loo . boo)

(pcase '(foo moo loo . boo)
  (`(,@list-var . ,other)
   (format "Got %S: %S" list-var other))). ; -> nil, expected “Got (foo moo loo): boo"

I also noticed that "Backquote-Style Patterns: Backquote Patterns.” is quite deep in the pcase doc structure, coming just after  “Extending ‘pcase’: Extending pcase.  Define new kinds of patterns.”.  Yet most pcase examples I come across in the wild use backquote patterns.  I don’t know if that’s an intentional arrangement, or was put in place before people had experience with pcase and voted with their key-taps for backquote style. 


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

* Re: Code for cond*
  2024-01-24 15:29       ` JD Smith
@ 2024-01-24 15:55         ` Stefan Monnier
  2024-01-24 16:02           ` Stefan Monnier
                             ` (2 more replies)
  0 siblings, 3 replies; 134+ messages in thread
From: Stefan Monnier @ 2024-01-24 15:55 UTC (permalink / raw)
  To: JD Smith; +Cc: Stefan Kangas, emacs-devel

> To explain this correctly, I’d need to understand where the mental
> model breaks down.  For example, I was surprised that this
> forward/reverse symmetry was not maintained:
>
> (let ((list-var '(foo moo loo))
>       (other 'boo))
>   `(,@list-var . ,other)). ; -> (foo moo loo . boo)
>
> (pcase '(foo moo loo . boo)
>   (`(,@list-var . ,other)
>    (format "Got %S: %S" list-var other))). ; -> nil, expected “Got (foo moo loo): boo"

Note that

    (let ((list-var '(foo moo))
          (other '(loo . boo)))
      `(,@list-var . ,other))

returns the same `(foo moo loo . boo)`, so there is not a unique way for
a pattern like `(,@list-var . ,other) to match a given list.
That's a large part of the reason why ,@ is not supported in Pcase's
backward patterns.

> I also noticed that "Backquote-Style Patterns: Backquote Patterns.” is
> quite deep in the pcase doc structure, coming just after  “Extending
> ‘pcase’: Extending pcase.  Define new kinds of patterns.”.  Yet most
> pcase examples I come across in the wild use backquote patterns.
> I don’t know if that’s an intentional arrangement, or was put in place
> before people had experience with pcase and voted with their key-taps
> for backquote style. 

I think it's a mistake due to the docs being written by people too
familiar with the implementation (where core Pcase does not include
backquote patterns).  I'd welcome a patch which corrects this, indeed.


        Stefan




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

* Re: Code for cond*
  2024-01-24 15:55         ` Stefan Monnier
@ 2024-01-24 16:02           ` Stefan Monnier
  2024-01-24 16:20           ` JD Smith
  2024-01-24 16:35           ` [External] : " Drew Adams
  2 siblings, 0 replies; 134+ messages in thread
From: Stefan Monnier @ 2024-01-24 16:02 UTC (permalink / raw)
  To: JD Smith; +Cc: Stefan Kangas, emacs-devel

> That's a large part of the reason why ,@ is not supported in Pcase's
> backward patterns.
  ^^^^^^^^
  backquote


        Stefan




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

* Re: Code for cond*
  2024-01-24 15:55         ` Stefan Monnier
  2024-01-24 16:02           ` Stefan Monnier
@ 2024-01-24 16:20           ` JD Smith
  2024-01-24 17:08             ` Stefan Monnier
  2024-01-24 16:35           ` [External] : " Drew Adams
  2 siblings, 1 reply; 134+ messages in thread
From: JD Smith @ 2024-01-24 16:20 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Stefan Kangas, emacs-devel



> On Jan 24, 2024, at 10:55 AM, Stefan Monnier <monnier@iro.umontreal.ca> wrote:
> 
>> To explain this correctly, I’d need to understand where the mental
>> model breaks down.  For example, I was surprised that this
>> forward/reverse symmetry was not maintained:
>> 
>> (let ((list-var '(foo moo loo))
>>      (other 'boo))
>>  `(,@list-var . ,other)). ; -> (foo moo loo . boo)
>> 
>> (pcase '(foo moo loo . boo)
>>  (`(,@list-var . ,other)
>>   (format "Got %S: %S" list-var other))). ; -> nil, expected “Got (foo moo loo): boo"
> 
> Note that
> 
>    (let ((list-var '(foo moo))
>          (other '(loo . boo)))
>      `(,@list-var . ,other))
> 
> returns the same `(foo moo loo . boo)`, so there is not a unique way for
> a pattern like `(,@list-var . ,other) to match a given list.
> That's a large part of the reason why ,@ is not supported in Pcase's
> backquote patterns.

Not a unique way without a policy, but could it not take the largest possible list at that position, ala regexp greedy matching?

Any other forward/reverse asymmetries you are aware of (other than the use of meaningful sub-lists with symbols like pred/and/or/guard for pcase to interpret)?


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

* Re: Code for cond*
  2024-01-24 12:39 ` Alan Mackenzie
  2024-01-24 14:43   ` Emanuel Berg
@ 2024-01-24 16:25   ` Manuel Giraud via Emacs development discussions.
  2024-01-25 14:01   ` Code for cond* - cond*-match, cond*-subpat and backtrack-aliases Alan Mackenzie
  2 siblings, 0 replies; 134+ messages in thread
From: Manuel Giraud via Emacs development discussions. @ 2024-01-24 16:25 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: Richard Stallman, emacs-devel

Alan Mackenzie <acm@muc.de> writes:

> Hello, Richard.
>
> On Wed, Jan 17, 2024 at 22:37:47 -0500, Richard Stallman wrote:
>> [[[ 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. ]]]
>
>> Here is the first draft of cond*.  I have tested some cases
>> but I ask others to help in testing it more thoroughly.
>
>> I invite constructive comments, bug reports, patches,
>> and suggestions.
>
> [ .... ]
>
> One thing I noticed was that the implementation makes no use of macros,
> except for a trivial macro which "renames" cond* itself.
>
> This is in contrase to pcase.el, which uses macros extensively,
> requiring lisp/emacs-lisp/macroexp.el to work.  In its turn macroexp.el
> uses pcase.  This leads to an unlovely artifice at bootstrap time.
>
> The macro in cond* can be expanded by Fmacroexpand in src/eval.c.  Thus,
> at bootstrap, condstar.el could simply be loaded before macroexpand.el.
>
> This is a Good Thing, and I'm sure it's not an accident.  Please keep
> this feature as cond* get amended and further developed.

This argument and the Po's one about the relatively small size of
condstar seem like good ones to me to have this option into Emacs.

But I also feel for the maintenance argument of Stefan K.
(just my two cents here of course)
-- 
Manuel Giraud



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

* RE: [External] : Re: Code for cond*
  2024-01-24  4:49   ` JD Smith
  2024-01-24  9:45     ` Stefan Kangas
@ 2024-01-24 16:30     ` Drew Adams
  2024-02-01  8:56       ` Madhu
  2024-01-25  3:16     ` Madhu
  2 siblings, 1 reply; 134+ messages in thread
From: Drew Adams @ 2024-01-24 16:30 UTC (permalink / raw)
  To: JD Smith, Stefan Monnier; +Cc: emacs-devel@gnu.org

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

Indeed, this is the place for the pcase doc to
_start_.  Unfortunately, the doc doesn't do that.

There _is_ a pcase doc _subnode_ that covers such
pattern-matching destructuring, but this should
be up-front.  It's _the most important_ feature
to point out and make clear.  It's the easiest to
grasp and the most useful for understanding the
rest.

The subnode covering destructuring is the very
_last_ one, "Destructuring with pcase Patterns":

https://www.gnu.org/software/emacs/manual/html_node/elisp/Destructuring-with-pcase-Patterns.html
___

I specifically suggested such doc improvements
in an enhancement request, but they were
unfortunately summarily dismissed just a few
minutes after the suggestion was received:

https://debbugs.gnu.org/cgi/bugreport.cgi?bug=68029#13

About this I said, e.g.:

  Such advantages should start with DESTRUCTURING -
  the ONE thing you _don't_ have built-in with the
  existing Elisp binding forms or conditional forms.

  Start with a simple destructuring example:
  `case'-like, but with destructuring.  That's my
  advice.

  Destructuring is the easiest & most powerful `pcase'
  feature to introduce, and it's not really shown.
  It's glossed over, at best.  Show beginners that, to
  start with.

  In a nutshell, `pase-let' vs `let' is really the
  place to start.  _Then_ add conditional control into
  the mix.

  [The current top-level, beginning `pcase' doc is] as
  if someone only tried to explain away some of what
  is particularly confusing about `pcase', instead of
  teaching the strengths of `pcase'.

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

* Re: Code for cond*
  2024-01-24 15:28                         ` Emanuel Berg
@ 2024-01-24 16:30                           ` Ihor Radchenko
  2024-01-24 16:34                             ` Emanuel Berg
  2024-01-25  9:06                           ` Po Lu
  1 sibling, 1 reply; 134+ messages in thread
From: Ihor Radchenko @ 2024-01-24 16:30 UTC (permalink / raw)
  To: Emanuel Berg; +Cc: emacs-devel

Emanuel Berg <incal@dataswamp.org> writes:

>> I am not sure if pointing this out is constructive.
>> Coming from the same crowd or not, the useful arguments
>> should be considered.
>
> Yes but we heard from you that the goal was to replace
> `pcase'. It was the view of two people. The third is not here,
> he is staying low?

This is not my view. This is my understanding of RMS' intent.

My view is "anything that improves Emacs is good". I do not have
more specific goals in this thread.

-- 
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>



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

* Re: Code for cond*
  2024-01-24 16:30                           ` Ihor Radchenko
@ 2024-01-24 16:34                             ` Emanuel Berg
  0 siblings, 0 replies; 134+ messages in thread
From: Emanuel Berg @ 2024-01-24 16:34 UTC (permalink / raw)
  To: emacs-devel

Ihor Radchenko wrote:

>>> I am not sure if pointing this out is constructive.
>>> Coming from the same crowd or not, the useful arguments
>>> should be considered.
>>
>> Yes but we heard from you that the goal was to replace
>> `pcase'. It was the view of two people. The third is not
>> here, he is staying low?
>
> This is not my view. This is my understanding of
> RMS' intent.
>
> My view is "anything that improves Emacs is good". I do not
> have more specific goals in this thread.

Sorry for the confusion, you are right, I replied to your post
technically but actually said that to the person _you_
were quoting.

-- 
underground experts united
https://dataswamp.org/~incal




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

* RE: [External] : Re: Code for cond*
  2024-01-24 15:55         ` Stefan Monnier
  2024-01-24 16:02           ` Stefan Monnier
  2024-01-24 16:20           ` JD Smith
@ 2024-01-24 16:35           ` Drew Adams
  2 siblings, 0 replies; 134+ messages in thread
From: Drew Adams @ 2024-01-24 16:35 UTC (permalink / raw)
  To: Stefan Monnier, JD Smith; +Cc: Stefan Kangas, emacs-devel@gnu.org

> > I also noticed that "Backquote-Style Patterns: Backquote Patterns.”
> > is quite deep in the pcase doc structure, coming just after  “Extending
> > ‘pcase’: Extending pcase.  Define new kinds of patterns.”.  Yet most
> > pcase examples I come across in the wild use backquote patterns.
> > I don’t know if that’s an intentional arrangement, or was put in place
> > before people had experience with pcase and voted with their key-taps
> > for backquote style.
> 
> I think it's a mistake due to the docs being
> written by people too familiar with the
> implementation (where core Pcase does not include
> backquote patterns).  I'd welcome a patch which
> corrects this, indeed.

That's what I guessed too, in my doc suggestion
that was roundly dismissed:

  It's as if someone only tried to explain away
  some of what is particularly confusing about
  `pcase', instead of teaching the strengths of
  `pcase'.

  Looks like doc written after a user complained
  that some previous `pcase' doc didn't explain
  some complicated aspects, so an attempt was
  made to do only that, skipping the basics.

  I don't claim that's really how we got the doc
  we got.  I'm just saying it smells like that.

https://debbugs.gnu.org/cgi/bugreport.cgi?bug=68029#13

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

* Re: Code for cond*
  2024-01-24 16:20           ` JD Smith
@ 2024-01-24 17:08             ` Stefan Monnier
  0 siblings, 0 replies; 134+ messages in thread
From: Stefan Monnier @ 2024-01-24 17:08 UTC (permalink / raw)
  To: JD Smith; +Cc: Stefan Kangas, emacs-devel

>> returns the same `(foo moo loo . boo)`, so there is not a unique way for
>> a pattern like `(,@list-var . ,other) to match a given list.
>> That's a large part of the reason why ,@ is not supported in Pcase's
>> backquote patterns.
> Not a unique way without a policy, but could it not take the largest
> possible list at that position, ala regexp greedy matching?

Of course, if you absolutely want that feature, you can define a meaning
for it.  But it does make it somewhat different in nature to the rest of
the patterns.  Another difference is that it will allocate new objects
rather than just select fields from the object under scrutiny.

BTW, this is all implementable right now without any change to
`pcase.el`: just write the corresponding `pcase-defmacro` :-)


        Stefan




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

* Re: Code for cond*
  2024-01-20  3:39   ` Richard Stallman
  2024-01-24 12:37     ` Po Lu
@ 2024-01-24 19:12     ` Alan Mackenzie
  2024-01-27  3:35       ` Richard Stallman
  1 sibling, 1 reply; 134+ messages in thread
From: Alan Mackenzie @ 2024-01-24 19:12 UTC (permalink / raw)
  To: Richard Stallman; +Cc: Emanuel Berg, emacs-devel

Hello, Richard.

On Fri, Jan 19, 2024 at 22:39:39 -0500, Richard Stallman wrote:
> [[[ 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.  I fixed the bugs you mentioned; elint did not show any
> others.  I always ran it interpreted to facilitate debugging, so I had
> never compiled it.

> Here is the new version.

> To test it thoroughly is more that I can try to do.  Could people please
> try using cond*?

I've read the code, briefly, and there're one or two things I'd like to
comment on.

In cond*-convert-condition, in

>                (if uncondit-clauses
>                    ;; bind* starts a non-exit clause.
>                    ;; Run the TRUE-EXPS.
>                    ;; Then always go on to run the UNCONDIT-CLAUSES.
>                    `(progn
>                       (if ,last-value
>                           (let* ,(cdr condition)
>                             . ,true-exps))
>                       (let* ,(cdr condition)
>                         ,(cond*-convert uncondit-clauses)))

, in the generated code, the form (let* ,(cdr condition) ...) sometimes
gets evaluated twice.  Surely this is a Bad Thing.  Or am I missing
something?

In cond*-match, you use gensyms.  I've not spent enough time
understanding the code, but would it be possible, somehow, to avoid
these?  The gensyms in pcase make the macro expanded code hard to read.
There one frequently has to distinguish between x293, x295, x296, ....
These symbols have no obvious relationship to the original code.  Worse,
they're not repeatable; because of the way gensym works, what was x293
last time will be x352 next time around, making it difficult to compare
two expansions.

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

-- 
Alan Mackenzie (Nuremberg, Germany).



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

* Re: Code for cond*
  2024-01-24  4:49   ` JD Smith
  2024-01-24  9:45     ` Stefan Kangas
  2024-01-24 16:30     ` Drew Adams
@ 2024-01-25  3:16     ` Madhu
  2024-01-25 13:57       ` Stefan Monnier
  2 siblings, 1 reply; 134+ messages in thread
From: Madhu @ 2024-01-25  3:16 UTC (permalink / raw)
  To: emacs-devel

* JD Smith <1AD5807F-91F7-4B92-BCB0-D0FEA904A75D @gmail.com> :
Wrote on 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.

[Using an example from another thread]

```
  (t (let ((temp (cadr whatsis)))
       (cond ((eq temp 'foo))
             ...
```

The idiom i've seen from 80s code is to explicitly pull the bindings to
the outermost scope (like cl loop  does when expanded)

```
(let (temp)
  (cond ((eq (setq temp (cadr whatsis)) 'foo))
   ...
```

This solves one indentation problem and creates another.

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

[...]

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




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

* Re: Code for cond*
  2024-01-24 15:28                         ` Emanuel Berg
  2024-01-24 16:30                           ` Ihor Radchenko
@ 2024-01-25  9:06                           ` Po Lu
  2024-01-25  9:55                             ` Alfred M. Szmidt
  2024-01-25 10:30                             ` Eli Zaretskii
  1 sibling, 2 replies; 134+ messages in thread
From: Po Lu @ 2024-01-25  9:06 UTC (permalink / raw)
  To: emacs-devel

Emanuel Berg <incal@dataswamp.org> writes:

>> I am not sure if pointing this out is constructive.
>> Coming from the same crowd or not, the useful arguments
>> should be considered.

It is NEVER useful to claim that:

  "Feature X is not useful.  Let's toss it to the dogs!"

Replace "feature X" with "the children's bread", and no decent person
will disagree.  Yet somehow that is acceptable with ELPA as the dogs.

Whether consciously or not, ELPA has become a repository where code may
be abandoned to bitrot in peace, so when someone on this list produces a
list of reasons he dislikes such and such in support of moving it to
ELPA, what he really means is that his objections are so strong he never
wishes to see it again, but it is not seemly or persuasive to express
such objections without veiling them in euphemisms.  Since this also
avoids causing the proposer too much disappointment, it achieves the
desired goal of frustrating the proposal with the absolute minimum level
of fuss.

I'm sorry if this came out sounding quite harsh, but it is how I view
this farce, and when the ELPA tactic is used against an elderly cancer
patient, it is really beyond the pale.

> Yes but we heard from you that the goal was to replace
> `pcase'. It was the view of two people. The third is not here,
> he is staying low?

Who is "you" here?

> If you say it right out, that is your agenda, so if the goal
> is the replace pcase, we see the install of cond* as a first
> step on that plan.

There is no hidden agenda behind the installation of cond*.



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

* Re: Code for cond*
  2024-01-24 12:15         ` Alan Mackenzie
  2024-01-24 12:28           ` Emanuel Berg
@ 2024-01-25  9:10           ` Po Lu
  2024-01-25 11:56             ` Emanuel Berg
  2024-01-25 23:32           ` Stefan Kangas
  2 siblings, 1 reply; 134+ messages in thread
From: Po Lu @ 2024-01-25  9:10 UTC (permalink / raw)
  To: Alan Mackenzie
  Cc: Stefan Kangas, rms, joaotavora, acorallo, emacs-devel, monnier,
	eliz

Alan Mackenzie <acm@muc.de> writes:

> That's just a way of ensuring it never comes to anything and just gets
> forgotten about.  If that is done, it will be impossible to replace any
> of the pcase uses in Emacs with cond*.

My thoughts exactly.

> A better way of doing this would be to create a feature branch, which
> can later be merged into master once its advantages become clear.

The same problems that apply to ELPA also apply to feature branches,
except that feature branches are far more demanding in regard to
maintenance.

Thanks.



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

* Re: Code for cond*
  2024-01-25  9:06                           ` Po Lu
@ 2024-01-25  9:55                             ` Alfred M. Szmidt
  2024-01-25 10:38                               ` Eli Zaretskii
  2024-01-25 11:29                               ` Po Lu
  2024-01-25 10:30                             ` Eli Zaretskii
  1 sibling, 2 replies; 134+ messages in thread
From: Alfred M. Szmidt @ 2024-01-25  9:55 UTC (permalink / raw)
  To: Po Lu; +Cc: emacs-devel

If you think COND* is better, argue for it using technical arguments.
Someones health issues have literally no bearing on if something is to
be added, to live in ELPA or something else.

So please stop don't continue this thread using that line of emotional
tangents, it is not useful.



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

* Re: Code for cond*
  2024-01-25  9:06                           ` Po Lu
  2024-01-25  9:55                             ` Alfred M. Szmidt
@ 2024-01-25 10:30                             ` Eli Zaretskii
  2024-01-25 11:27                               ` Emanuel Berg
  1 sibling, 1 reply; 134+ messages in thread
From: Eli Zaretskii @ 2024-01-25 10:30 UTC (permalink / raw)
  To: Po Lu; +Cc: emacs-devel

> From: Po Lu <luangruo@yahoo.com>
> Date: Thu, 25 Jan 2024 17:06:20 +0800
> 
> Whether consciously or not, ELPA has become a repository where code may
> be abandoned to bitrot in peace, so when someone on this list produces a
> list of reasons he dislikes such and such in support of moving it to
> ELPA, what he really means is that his objections are so strong he never
> wishes to see it again, but it is not seemly or persuasive to express
> such objections without veiling them in euphemisms.  Since this also
> avoids causing the proposer too much disappointment, it achieves the
> desired goal of frustrating the proposal with the absolute minimum level
> of fuss.
> 
> I'm sorry if this came out sounding quite harsh, but it is how I view
> this farce

This view is incorrect.  We don't use ELPA as a dumping ground for
packages "we don't want to see, ever".



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

* Re: Code for cond*
  2024-01-25  9:55                             ` Alfred M. Szmidt
@ 2024-01-25 10:38                               ` Eli Zaretskii
  2024-01-25 11:29                               ` Po Lu
  1 sibling, 0 replies; 134+ messages in thread
From: Eli Zaretskii @ 2024-01-25 10:38 UTC (permalink / raw)
  To: Alfred M. Szmidt; +Cc: luangruo, emacs-devel

> From: "Alfred M. Szmidt" <ams@gnu.org>
> Cc: emacs-devel@gnu.org
> Date: Thu, 25 Jan 2024 04:55:24 -0500
> 
> If you think COND* is better, argue for it using technical arguments.
> Someones health issues have literally no bearing on if something is to
> be added, to live in ELPA or something else.
> 
> So please stop don't continue this thread using that line of emotional
> tangents, it is not useful.

I would actually ask everyone except the Emacs maintainers to avoid
wording which could be interpreted as expressing the views or policies
of the project.  Many people who read this list only occasionally
don't know who is and who isn't speaking for the project, and thus
they misinterpret messages that express opinions to be policy or
something that's about to become policy.  This then causes
misunderstandings, unnecessary messages, potential flame wars,
articles in other forums that spread fake news about us, etc.  Please
try to avoid those negative effects by wording your opinions
correspondingly and/or making clear you don't speak for the project.

TIA



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

* Re: Code for cond*
  2024-01-25 10:30                             ` Eli Zaretskii
@ 2024-01-25 11:27                               ` Emanuel Berg
  0 siblings, 0 replies; 134+ messages in thread
From: Emanuel Berg @ 2024-01-25 11:27 UTC (permalink / raw)
  To: emacs-devel

Eli Zaretskii wrote:

> This view is incorrect. We don't use ELPA as a dumping
> ground for packages "we don't want to see, ever".

On the contrary, we put things that we care about in ELPA.

It is also a good place for people to be able to test stuff at
their own discretion and in an undramatic way.

Where software can mature over time, loose bugs, and find its
place in the ways people are thinking about it.

-- 
underground experts united
https://dataswamp.org/~incal




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

* Re: Code for cond*
  2024-01-25  9:55                             ` Alfred M. Szmidt
  2024-01-25 10:38                               ` Eli Zaretskii
@ 2024-01-25 11:29                               ` Po Lu
  2024-01-25 12:04                                 ` Emanuel Berg
  2024-01-25 13:19                                 ` Alfred M. Szmidt
  1 sibling, 2 replies; 134+ messages in thread
From: Po Lu @ 2024-01-25 11:29 UTC (permalink / raw)
  To: Alfred M. Szmidt; +Cc: emacs-devel

"Alfred M. Szmidt" <ams@gnu.org> writes:

> If you think COND* is better, argue for it using technical arguments.
> Someones health issues have literally no bearing on if something is to
> be added, to live in ELPA or something else.

The technical arguments have already been argued to exhaustion, and only
subjective issues remain outstanding, which a consensus will never form
around.  So what I see is that people are now clamoring to have the
feature they find to their distaste confined to ELPA on the grounds that
there is already a feature significantly overlapping in functionality in
core.  It is no coincidence that the feature being targeted embodies the
preferences of the opposing side, and between this and the fact that
such feature is a compromise enabling those on both sides to coexist, it
begins to appear that one side is not willing to settle for any solution
where their preferred macro is not the only option (which is the ideal
outcome, really, considering the alternatives).

With such features, the possibility of hosting them on ELPA should not
have been mentioned at all.  It is a guarantee of obscurity, not least
for potential users in Emacs core, for whom it is practically a death
sentence.



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

* Re: Code for cond*
  2024-01-25  9:10           ` Po Lu
@ 2024-01-25 11:56             ` Emanuel Berg
  2024-01-25 13:21               ` Po Lu
  0 siblings, 1 reply; 134+ messages in thread
From: Emanuel Berg @ 2024-01-25 11:56 UTC (permalink / raw)
  To: emacs-devel

Po Lu wrote:

>> That's just a way of ensuring it never comes to anything
>> and just gets forgotten about. If that is done, it will be
>> impossible to replace any of the pcase uses in Emacs with
>> cond*.
>
> My thoughts exactly.

`pcase' has been around since 2010.

It is used 2213 times in the Emacs source.

To say that, after 13 years of service, it should be replaced
by virtually untested software with no clear advantage, is
just a hooligan move that will alienate people for no reason.

It is okay to replace good software with better, sure, but
especially in circumstances like this one would then have to
be able to make a really strong and convincing case for the
proposed solution.

   1551 pcase
    192 pcase-let
    143 pcase-dolist
     98 pcase-let*
     45 pcase-lambda
     40 pcase-exhaustive
     27 pcase-defmacro
     26 pcase-tests
     20 pcase-setq
     19 pcase-macroexpander
     10 pcase-vars
      7 pcase-patterns
      6 pcase-expand
      5 pcase-transform
      5 pcase-mutually
      4 pcase-bindings
      3 pcase-memoize
      3 pcase-compile
      2 pcase-used
      2 pcase-slot
      2 pcase-map
      1 pcase-matches
      1 pcase-default
      1 pcase-bug
Total: 2213

-- 
underground experts united
https://dataswamp.org/~incal




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

* Re: Code for cond*
  2024-01-25 11:29                               ` Po Lu
@ 2024-01-25 12:04                                 ` Emanuel Berg
  2024-01-25 13:32                                   ` Po Lu
  2024-01-25 13:19                                 ` Alfred M. Szmidt
  1 sibling, 1 reply; 134+ messages in thread
From: Emanuel Berg @ 2024-01-25 12:04 UTC (permalink / raw)
  To: emacs-devel

Po Lu wrote:

> The technical arguments have already been argued to
> exhaustion, and only subjective issues remain outstanding,
> which a consensus will never form around.

If a consensus cannot be reached, obey tradition.

And don't say your proposed, untested solution should replace
something that has been operational for over a decade and is
used even as we speak.

What you can say is "Hey, we have something interesting here,
take a look, hopefully it will gain traction if people are
appealed by it".

But you are not saying that because you yourself are not
confident cond* really has any such advantages over `pcase' to
motivate anything close to this kind of
confrontational approach.

-- 
underground experts united
https://dataswamp.org/~incal




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

* Re: Code for cond*
  2024-01-25 11:29                               ` Po Lu
  2024-01-25 12:04                                 ` Emanuel Berg
@ 2024-01-25 13:19                                 ` Alfred M. Szmidt
  2024-01-25 13:43                                   ` Emanuel Berg
  1 sibling, 1 reply; 134+ messages in thread
From: Alfred M. Szmidt @ 2024-01-25 13:19 UTC (permalink / raw)
  To: Po Lu; +Cc: emacs-devel

ELPA is part of Emacs, suggesting it for addition of things is fair
game.  You make good arguments that it might make something "obscure",
there are other arguments in favor of why ELPA might not be a good
place (e.g., Alan had once regarding bootstrapping where PCOND is
slightly trickier than COND*).  But it is a technical suggestion, and
one that is perfectly valid -- Eli, Richard, or other Emacs
maintainers are quite capable of deciding what to do on the topic.

That some people clamour to whatever sillyness, is also really not a
problem -- again, the Emacs maintainers are quite capable on figuring
out or asking questions.

One nice thing about Emacs is that it is quite easy to get things into
Emacs, so I think you're worrying too much about the situation.



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

* Re: Code for cond*
  2024-01-25 11:56             ` Emanuel Berg
@ 2024-01-25 13:21               ` Po Lu
  2024-01-25 13:56                 ` Emanuel Berg
  0 siblings, 1 reply; 134+ messages in thread
From: Po Lu @ 2024-01-25 13:21 UTC (permalink / raw)
  To: emacs-devel

Emanuel Berg <incal@dataswamp.org> writes:

> `pcase' has been around since 2010.
>
> It is used 2213 times in the Emacs source.
>
> To say that, after 13 years of service, it should be replaced
> by virtually untested software with no clear advantage, is
> just a hooligan move that will alienate people for no reason.

Am I to understand that allowing package authors to replace instances of
pcase with cond* at their own discretion is a "hooligan move?"  If so,
I've undoubtedly missed several substantial developments in the area of
etiquette that have transformed choice into villainy.



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

* Re: Code for cond*
  2024-01-25 12:04                                 ` Emanuel Berg
@ 2024-01-25 13:32                                   ` Po Lu
  2024-01-25 14:08                                     ` Emanuel Berg
  0 siblings, 1 reply; 134+ messages in thread
From: Po Lu @ 2024-01-25 13:32 UTC (permalink / raw)
  To: emacs-devel

Emanuel Berg <incal@dataswamp.org> writes:

> If a consensus cannot be reached, obey tradition.
>
> And don't say your proposed, untested solution should replace
> something that has been operational for over a decade and is
> used even as we speak.
>
> What you can say is "Hey, we have something interesting here,
> take a look, hopefully it will gain traction if people are
> appealed by it".
>
> But you are not saying that because you yourself are not
> confident cond* really has any such advantages over `pcase' to
> motivate anything close to this kind of
> confrontational approach.

I can't so much as understand pcase forms, let alone debate the fine
technical details that certain participants in this thread insist
demonstrate the superiority of pcase over cond*.  That is a glaring
problem in its own right!

But if you read my words carefully, it will be apparent that all I have
asked is that people refrain from contriving reasons to exclude cond*
from Emacs.  Richard already made up his mind, yet with the perplexing
and disproportionate flurry of hostility which has been provoked, all
that now hangs in the balance.



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

* Re: Code for cond*
  2024-01-25 13:19                                 ` Alfred M. Szmidt
@ 2024-01-25 13:43                                   ` Emanuel Berg
  2024-01-25 14:31                                     ` Eli Zaretskii
  0 siblings, 1 reply; 134+ messages in thread
From: Emanuel Berg @ 2024-01-25 13:43 UTC (permalink / raw)
  To: emacs-devel

Alfred M. Szmidt wrote:

> One nice thing about Emacs is that it is quite easy to get
> things into Emacs, so I think you're worrying too much about
> the situation.

I think most people didn't worry when it was just about
pushing cond*.

But when it was then said outright that the purpose of cond*
was to replace `pcase' I think people started to object
a little bit - will this not lead to conflicts between people,
a lot of time spent on doing it, with possibly many bugs
introduced in the process to code that has worked for ages,
and just in general a lot of time wasted on this which one
would hope the maintainers could and would do much better with
for everyone's advantage?

The kind of confrontational "might makes right" attitude that
we have heard also feels very out of place, everyone is used
to things being throughly discussed with a clear focus on the
technology itself.

Can we at least agree on a list with what advantages cond*
would bring?

If we can't, sure, feel free to do whatever you want with it
including including it in the Emacs core source - but doing so
saying before it even happened explicitely that it should
replace pcase - that just feels like a completely bizarre
thing to do at this point, sorry.

-- 
underground experts united
https://dataswamp.org/~incal




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

* Re: Code for cond*
  2024-01-25 13:21               ` Po Lu
@ 2024-01-25 13:56                 ` Emanuel Berg
  0 siblings, 0 replies; 134+ messages in thread
From: Emanuel Berg @ 2024-01-25 13:56 UTC (permalink / raw)
  To: emacs-devel

Po Lu wrote:

> Am I to understand that allowing package authors to replace
> instances of pcase with cond* at their own discretion is
> a "hooligan move?"

The problem is that so many files don't have their own active
maintainer. If that was the case, that would be much better,
yes. And it would decrease the burden of maintaining so many
files for just a small bunch of people.

Now, if those people are bent on the idea of replacing `pcase'
with cond* all over and across those files, that would be
a huge waste of time and effort, it wouldn't make Emacs any
better, various veteran contributors would be alienated, and
it would also be a pretty ugly purge just in general - for,
again, very unclear reasons.

-- 
underground experts united
https://dataswamp.org/~incal




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

* Re: Code for cond*
  2024-01-25  3:16     ` Madhu
@ 2024-01-25 13:57       ` Stefan Monnier
  2024-01-25 15:17         ` JD Smith
  0 siblings, 1 reply; 134+ messages in thread
From: Stefan Monnier @ 2024-01-25 13:57 UTC (permalink / raw)
  To: Madhu; +Cc: emacs-devel

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

Nice.  Would `var` and `dvar` scope over just the first branch or also the
other ones?
Technically, there is no reason for them not to be visible in the other
branches, so that's what I would go for.

Personally, I was thinking of a syntax like

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

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

     (t nil))

with the intention of replacing `cond` without introducing
any incompatibility.


        Stefan




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

* Re: Code for cond* - cond*-match, cond*-subpat and backtrack-aliases
  2024-01-24 12:39 ` Alan Mackenzie
  2024-01-24 14:43   ` Emanuel Berg
  2024-01-24 16:25   ` Manuel Giraud via Emacs development discussions.
@ 2024-01-25 14:01   ` Alan Mackenzie
  2024-01-29  3:19     ` Richard Stallman
  2024-01-29  3:19     ` Richard Stallman
  2 siblings, 2 replies; 134+ messages in thread
From: Alan Mackenzie @ 2024-01-25 14:01 UTC (permalink / raw)
  To: Richard Stallman; +Cc: emacs-devel

Hello again, Richard.

On Wed, Jan 24, 2024 at 12:39:54 +0000, Alan Mackenzie wrote:
> On Wed, Jan 17, 2024 at 22:37:47 -0500, Richard Stallman wrote:
> > [[[ 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. ]]]

> > Here is the first draft of cond*.  I have tested some cases
> > but I ask others to help in testing it more thoroughly.

> > I invite constructive comments, bug reports, patches,
> > and suggestions.

I've found some more things I'm not happy about in the code.

1/- There are typos in the doc string of cond*-subpat.
2/- There are three places where backtrack-aliases is used where I think
  it should be (cdr backtrack-aliases).
3/- In cond*-subpat, there are bindings for (gensym "ba") which seem
  unable to be referenced by anything.  I think I'm missing something
  here.  As I said yesterday, I'd be happier if gensyms could be
  avoided.  I don't know if this is possible or practicable.
4/- There are several place where "\\>" is appended to a regexp.  Would
  "\\_>" ("end of symbol"), which we've had in Emacs for 10 or 15 years
  now, perhaps be better?

I've included a patch for the first two of these things below.

There are other typos in the code, too, and I think it highly unlikely
that it is bug free, yet.  For cond* to be successful, it will need an
extensive test file.  Possibly we could copy the pcase test file (at
test/lisp/emacs-lisp/pcase-tests.el) and adapt it to cond*.

[ .... ]


--- stallman.20240119.el	2024-01-25 13:31:15.049200457 +0000
+++ stallman.20240125.el	2024-01-25 13:36:44.454224261 +0000
@@ -200,7 +200,7 @@
     ;; Run TRUE-EXPS if match succeeded.  Bind our bindings around it.
     (setq expression
           `(if ,expression
-               ,(if (not (and backtrack-aliases (null uncondit-clauses)))
+               ,(if (not (and (cdr backtrack-aliases) (null uncondit-clauses)))
                     ;; Bind these here if there are no UNCONDIT-CLAUSES.
                     `(let ,(mapcar 'cdr (cdr backtrack-aliases))
                        (let* ,(car raw-result)
@@ -219,7 +219,7 @@
                       (let* ,(car raw-result)
                         ,(cond*-convert uncondit-clauses)))))
     ;; If there are backtrack aliases, bind them around the UNCONDIT-CLAUSES.
-    (if (and backtrack-aliases uncondit-clauses)
+    (if (and (cdr backtrack-aliases) uncondit-clauses)
       (setq expression `(let ,(mapcar 'cdr (cdr backtrack-aliases))
                           ,expression)))
     ;; If we used a gensym, add code to bind it.
@@ -254,16 +254,16 @@
 ;;; ??? Probably should optimize the `nth' calls in handling `list'.
 
 (defun cond*-subpat (subpat cdr-safe bindings backtrack-aliases data)
-  "Generate code to match ibe subpattern within `match*'.
+  "Generate code to match the subpattern within `match*'.
 SUBPAT is the subpattern to handle.
 CDR-SAFE if true means don't verify there are no extra elts in a list.
 BINDINGS is the list of bindings made by
 the containing and previous subpatterns of this pattern.
 Each element of BINDINGS must have the frm (VAR VALUE).
-BACKTRACK-ALIASES is used to pass adta uward.  Initial call should
+BACKTRACK-ALIASES is used to pass data upward.  Initial call should
 pass (list).  The cdr of this collects backtracking aliases made for
 variables boung within (or...) patterns so that the caller
-dna bind them etc.
+can bind them etc.
 DATA is the expression for the data that this subpattern is
 supposed to match against.
 
@@ -290,7 +290,7 @@
              ;; for backtracking, just use that.
              (let ((this-alias (assq subpat (cdr backtrack-aliases))))
                (if this-alias (cdr this-alias)
-                 (if backtrack-aliases
+                 (if (cdr backtrack-aliases)
                      ;; Inside or subpattern but this symbol has no alias,
                      ;; make one for it.
                      (progn (setcdr backtrack-aliases (cons (cons subpat (gensym "ba"))

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

-- 
Alan Mackenzie (Nuremberg, Germany).



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

* Re: Code for cond*
  2024-01-25 13:32                                   ` Po Lu
@ 2024-01-25 14:08                                     ` Emanuel Berg
  0 siblings, 0 replies; 134+ messages in thread
From: Emanuel Berg @ 2024-01-25 14:08 UTC (permalink / raw)
  To: emacs-devel

Po Lu wrote:

>> What you can say is "Hey, we have something interesting
>> here, take a look, hopefully it will gain traction if
>> people are appealed by it".
>>
>> But you are not saying that because you yourself are not
>> confident cond* really has any such advantages over `pcase'
>> to motivate anything close to this kind of
>> confrontational approach.
>
> I can't so much as understand pcase forms, let alone debate
> the fine technical details that certain participants in this
> thread insist demonstrate the superiority of pcase over
> cond*.

It can be and is sometimes pretty unclear but it is clear
enough that cond* is not superior to `pcase' in any way that
even remotely motivates this kind of confrontational attitude.

To propose pcase should be replaced by it is bizarre at
this point.

> But if you read my words carefully, it will be apparent that
> all I have asked is that people refrain from contriving
> reasons to exclude cond* from Emacs. Richard already made up
> his mind [...]

To include cond*, or to have a grandiose plan to have it
replace pcase?

Because those are two different things. If the plan is to kick
out pcase I'm sure there is a plan to kick out cl-lib as well,
since the last time we heard about this, what we first heard
wasn't of pcase, but cl-lib.

Only when it become clear almost no one in the Emacs community
was in favor of kicking out cl-lib the focus switched
to pcase.

Hey, include whatever you want and let it play out anyway it
happens. But grandiose politics with tailored software and PR
campaigns to support it - no thanks.

-- 
underground experts united
https://dataswamp.org/~incal




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

* Re: Code for cond*
  2024-01-25 13:43                                   ` Emanuel Berg
@ 2024-01-25 14:31                                     ` Eli Zaretskii
  2024-01-25 15:02                                       ` Emanuel Berg
  0 siblings, 1 reply; 134+ messages in thread
From: Eli Zaretskii @ 2024-01-25 14:31 UTC (permalink / raw)
  To: Emanuel Berg; +Cc: emacs-devel

> From: Emanuel Berg <incal@dataswamp.org>
> Date: Thu, 25 Jan 2024 14:43:39 +0100
> 
> I think most people didn't worry when it was just about
> pushing cond*.
> 
> But when it was then said outright that the purpose of cond*
> was to replace `pcase' I think people started to object
> a little bit

That worry is unnecessary, since none of those who said that are in a
position to make these decisions.

IOW, you need to know to whom to listen on this list, if you are going
to take what people say to heart.



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

* Re: Code for cond*
  2024-01-25 14:31                                     ` Eli Zaretskii
@ 2024-01-25 15:02                                       ` Emanuel Berg
  2024-01-25 15:29                                         ` Eli Zaretskii
  0 siblings, 1 reply; 134+ messages in thread
From: Emanuel Berg @ 2024-01-25 15:02 UTC (permalink / raw)
  To: emacs-devel

Eli Zaretskii wrote:

>> I think most people didn't worry when it was just about
>> pushing cond*.
>> 
>> But when it was then said outright that the purpose of
>> cond* was to replace `pcase' I think people started to
>> object a little bit
>
> That worry is unnecessary, since none of those who said that
> are in a position to make these decisions.

Ah, good then.

> IOW, you need to know to whom to listen on this list, if you
> are going to take what people say to heart.

Good point, in admin/MAINTAINERS [1] it says

The (co-)maintainers of Emacs are:

	Eli Zaretskii <eliz@gnu.org>
	Stefan Kangas <stefankangas@gmail.com>

But now I'm confused, RMS isn't even mentioned in that file
yet we have heard several times the discussion is over, he has
already made up his mind etc.

But I admit I got the whole maintainer situation wrong,
I thought it was Alan Mackenzie, RMS, and you who were the
maintainer trio? And yes, that actually felt a little scary
with respect to the animosity to `cl-lib' and
`pcase' situation.

[1] GNU Emacs 30.0.50 (build 1, x86_64-pc-linux-gnu, cairo
    version 1.16.0) of 2024-01-09 [commit
    fa37950454f6c7536c00a1cc7ec6c00a8b4b4b6b]

-- 
underground experts united
https://dataswamp.org/~incal




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

* Re: Code for cond*
  2024-01-25 13:57       ` Stefan Monnier
@ 2024-01-25 15:17         ` JD Smith
  2024-01-25 15:37           ` JD Smith
  0 siblings, 1 reply; 134+ messages in thread
From: JD Smith @ 2024-01-25 15:17 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Madhu, emacs-devel



> On Jan 25, 2024, at 8:57 AM, Stefan Monnier <monnier@iro.umontreal.ca> wrote:
> 
>>> [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))
> 
> Nice.  Would `var` and `dvar` scope over just the first branch or also the
> other ones?
> Technically, there is no reason for them not to be visible in the other
> branches, so that's what I would go for.

It seems most intuitive to me for each clause to have its own scope, but mostly because active bindings made in sibling forms at the same depth seem non-intuitive and easy to overlook.

> Personally, I was thinking of a syntax like
> 
>    (my-cond
>     (:let var value)
>     (:let dvar (derived-from var))
>     ((has-the-right-stuff-p dvar)
>      (cons 'correct dvar))
> 
>     (:let foo value2)
>     (:let bar (1- foo))
>     ((< bar 0)
>      (cons 'incorrect bar))
> 
>     (t nil))
> 
> with the intention of replacing `cond` without introducing
> any incompatibility.

This is an interesting approach, and makes it much clearer when new bindings appear.  Mine is a different idea.  In my cond-let approach, just as for when-let, each binding in a clause’s binding group is tested, one by one, and the clause only succeeds if all the bindings are non-nil.  So var and dvar must be non-nil, and (has-the-right-stuff-p dvar) too.  To ensure this, in your version, I’d need something like:

(:let var value)
(:let dvar (and var (derived-from var))
((and var dvar (has-the-right-stuff-p dvar)) …)

This would get repetitive and error prone for a large number of bindings, not to mention awkward if some “bindings” in the middle are actually bound-variable-free tests like ((has-the-right-stuff-p dvar)).

The hypothetical cond-let makes it easy to build an arbitrary grouping of sequential when's/when-let's which short circuit as soon as the first clause body executes, no matter what that body returns.

So:

(cond-let
 (simple0 body0) ; a normal cond-style clause
 (bindings1 body1)
 (bindings2 body2)
 (bindings3 body3)
 (t fallthrough-body))

would be equivalent to:

(catch 'cond-let
  (when simple0 (throw 'cond-let body0))
  (when-let (bindings1) (throw 'cond-let body1))
  (when-let (bindings2) (throw 'cond-let body2))
  (when-let (bindings3) (throw 'cond-let body3))
  (throw 'cond-let fallthrough-body))

But a lot less wordy/repetitive ;). 


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

* Re: Code for cond*
  2024-01-25 15:02                                       ` Emanuel Berg
@ 2024-01-25 15:29                                         ` Eli Zaretskii
  2024-01-25 15:33                                           ` Alfred M. Szmidt
  2024-01-25 15:40                                           ` Emanuel Berg
  0 siblings, 2 replies; 134+ messages in thread
From: Eli Zaretskii @ 2024-01-25 15:29 UTC (permalink / raw)
  To: Emanuel Berg; +Cc: emacs-devel

> From: Emanuel Berg <incal@dataswamp.org>
> Date: Thu, 25 Jan 2024 16:02:17 +0100
> 
> Eli Zaretskii wrote:
> 
> > IOW, you need to know to whom to listen on this list, if you
> > are going to take what people say to heart.
> 
> Good point, in admin/MAINTAINERS [1] it says
> 
> The (co-)maintainers of Emacs are:
> 
> 	Eli Zaretskii <eliz@gnu.org>
> 	Stefan Kangas <stefankangas@gmail.com>
> 
> But now I'm confused, RMS isn't even mentioned in that file
> yet we have heard several times the discussion is over, he has
> already made up his mind etc.

Richard has the maintainer's cup for life ;-)

As for "made up his mind": that was about installing this macro, not
about replacing pcase with it everywhere.

> But I admit I got the whole maintainer situation wrong,
> I thought it was Alan Mackenzie, RMS, and you who were the
> maintainer trio?

No, the maintainers are in admin/MAINTAINERS.



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

* Re: Code for cond*
  2024-01-25 15:29                                         ` Eli Zaretskii
@ 2024-01-25 15:33                                           ` Alfred M. Szmidt
  2024-01-25 15:50                                             ` Eli Zaretskii
  2024-01-25 15:40                                           ` Emanuel Berg
  1 sibling, 1 reply; 134+ messages in thread
From: Alfred M. Szmidt @ 2024-01-25 15:33 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: incal, emacs-devel

   > But I admit I got the whole maintainer situation wrong,
   > I thought it was Alan Mackenzie, RMS, and you who were the
   > maintainer trio?

   No, the maintainers are in admin/MAINTAINERS.

Who maintains a GNU package is maintained in
fp.gnu.org:/gd/gnuorg/maintainers.bypkg, that is the offical list of
GNU maintainers.



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

* Re: Code for cond*
  2024-01-25 15:17         ` JD Smith
@ 2024-01-25 15:37           ` JD Smith
  2024-01-25 15:44             ` Alfred M. Szmidt
  2024-01-29  3:19             ` Richard Stallman
  0 siblings, 2 replies; 134+ messages in thread
From: JD Smith @ 2024-01-25 15:37 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Madhu, emacs-devel



> On Jan 25, 2024, at 10:17 AM, JD Smith <jdtsmith@gmail.com> wrote:
> 
> 
> 
>> On Jan 25, 2024, at 8:57 AM, Stefan Monnier <monnier@iro.umontreal.ca> wrote:
>> 
>>>> 
>>>> (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))
>> 
> 
>> Personally, I was thinking of a syntax like
>> 
>>   (my-cond
>>    (:let var value)
>>    (:let dvar (derived-from var))
>>    ((has-the-right-stuff-p dvar)
>>     (cons 'correct dvar))
>> 
>>    (:let foo value2)
>>    (:let bar (1- foo))
>>    ((< bar 0)
>>     (cons 'incorrect bar))
>> 
>>    (t nil))

And it perhaps goes without saying that the two approaches could be combined :) —

(cond-let
  (:let var-for-rest value)
  (simple0-using-var-for-rest body0) ; a normal cond-like clause
  (bindings1 body1-including-var-for-rest)
  
  (:let another-var (some-function var-for-rest))
  (bindings2-using-another-var body2)
  (bindings3 body3-using-another-var)
  (t fallthrough-body))




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

* Re: Code for cond*
  2024-01-25 15:29                                         ` Eli Zaretskii
  2024-01-25 15:33                                           ` Alfred M. Szmidt
@ 2024-01-25 15:40                                           ` Emanuel Berg
  1 sibling, 0 replies; 134+ messages in thread
From: Emanuel Berg @ 2024-01-25 15:40 UTC (permalink / raw)
  To: emacs-devel

Eli Zaretskii wrote:

>> But now I'm confused, RMS isn't even mentioned in that file
>> yet we have heard several times the discussion is over, he
>> has already made up his mind etc.
>
> Richard has the maintainer's cup for life ;-)
>
> As for "made up his mind": that was about installing this
> macro, not about replacing pcase with it everywhere.

Okay, is that so, maybe I have been punching myself out of
a paper bag then.

-- 
underground experts united
https://dataswamp.org/~incal




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

* Re: Code for cond*
  2024-01-25 15:37           ` JD Smith
@ 2024-01-25 15:44             ` Alfred M. Szmidt
  2024-01-25 16:00               ` JD Smith
  2024-01-25 16:05               ` Stefan Monnier
  2024-01-29  3:19             ` Richard Stallman
  1 sibling, 2 replies; 134+ messages in thread
From: Alfred M. Szmidt @ 2024-01-25 15:44 UTC (permalink / raw)
  To: JD Smith; +Cc: monnier, enometh, emacs-devel

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 1568 bytes --]




   > On Jan 25, 2024, at 10:17 AM, JD Smith <jdtsmith@gmail.com> wrote:
   > 
   > 
   > 
   >> On Jan 25, 2024, at 8:57 AM, Stefan Monnier <monnier@iro.umontreal.ca> wrote:
   >> 
   >>>> 
   >>>> (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))
   >> 
   > 
   >> Personally, I was thinking of a syntax like
   >> 
   >>   (my-cond
   >>    (:let var value)
   >>    (:let dvar (derived-from var))
   >>    ((has-the-right-stuff-p dvar)
   >>     (cons 'correct dvar))
   >> 
   >>    (:let foo value2)
   >>    (:let bar (1- foo))
   >>    ((< bar 0)
   >>     (cons 'incorrect bar))
   >> 
   >>    (t nil))

   And it perhaps goes without saying that the two approaches could be combined :) —

   (cond-let
     (:let var-for-rest value)

Is there a reason why using a keyword here?  How would multiple
bindings be handled -- or is that what the two forms:

  (:let foo value2)
  (:let bar (1- foo))

try to convey? 

This feels like stabbing your eyeballs with a rusty nail and pouring
salt ...  The first suggestion was much nicer, and clearer.

     (simple0-using-var-for-rest body0) ; a normal cond-like clause
     (bindings1 body1-including-var-for-rest)

     (:let another-var (some-function var-for-rest))
     (bindings2-using-another-var body2)
     (bindings3 body3-using-another-var)
     (t fallthrough-body))






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

* Re: Code for cond*
  2024-01-25 15:33                                           ` Alfred M. Szmidt
@ 2024-01-25 15:50                                             ` Eli Zaretskii
  2024-01-25 16:01                                               ` Alfred M. Szmidt
  0 siblings, 1 reply; 134+ messages in thread
From: Eli Zaretskii @ 2024-01-25 15:50 UTC (permalink / raw)
  To: Alfred M. Szmidt; +Cc: incal, emacs-devel

> From: "Alfred M. Szmidt" <ams@gnu.org>
> Cc: incal@dataswamp.org, emacs-devel@gnu.org
> Date: Thu, 25 Jan 2024 10:33:42 -0500
> 
>    > But I admit I got the whole maintainer situation wrong,
>    > I thought it was Alan Mackenzie, RMS, and you who were the
>    > maintainer trio?
> 
>    No, the maintainers are in admin/MAINTAINERS.
> 
> Who maintains a GNU package is maintained in
> fp.gnu.org:/gd/gnuorg/maintainers.bypkg, that is the offical list of
> GNU maintainers.

That file is not accessible to general public, though.



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

* Re: Code for cond*
  2024-01-25 15:44             ` Alfred M. Szmidt
@ 2024-01-25 16:00               ` JD Smith
  2024-01-25 16:05               ` Stefan Monnier
  1 sibling, 0 replies; 134+ messages in thread
From: JD Smith @ 2024-01-25 16:00 UTC (permalink / raw)
  To: Alfred M. Szmidt; +Cc: Stefan Monnier, Madhu, emacs-devel

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



> On Jan 25, 2024, at 10:44 AM, Alfred M. Szmidt <ams@gnu.org> wrote:
> 
> 
> 
> 
>> On Jan 25, 2024, at 10:17 AM, JD Smith <jdtsmith@gmail.com> wrote:
>> 
>> 
>> 
>>> On Jan 25, 2024, at 8:57 AM, Stefan Monnier <monnier@iro.umontreal.ca> wrote:
>>> 
>>>>> 
>>>>> (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))
>>> 
>> 
>>> Personally, I was thinking of a syntax like
>>> 
>>>  (my-cond
>>>   (:let var value)
>>>   (:let dvar (derived-from var))
>>>   ((has-the-right-stuff-p dvar)
>>>    (cons 'correct dvar))
>>> 
>>>   (:let foo value2)
>>>   (:let bar (1- foo))
>>>   ((< bar 0)
>>>    (cons 'incorrect bar))
>>> 
>>>   (t nil))
> 
>   And it perhaps goes without saying that the two approaches could be combined :) —
> 
>   (cond-let
>     (:let var-for-rest value)
> 
> Is there a reason why using a keyword here?  How would multiple
> bindings be handled -- or is that what the two forms:
> 
>  (:let foo value2)
>  (:let bar (1- foo))
> 
> try to convey? 

Stefan correct me if I’m missing the main point, but I think the difference motivating a special form like (:let …) comes from the desire to establish bindings for all the rest of the clauses, and to make it clear you are doing this.  I don’t know the right way to handle that (or how important it is).  The proposed cond* has that capability.

My cond-let idea has somewhat different goals, akin to a short-circuiting group of when + when-let’s.  Its clause syntax is just a straightforward copy of when-let; hence no new syntax to learn.


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

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

* Re: Code for cond*
  2024-01-25 15:50                                             ` Eli Zaretskii
@ 2024-01-25 16:01                                               ` Alfred M. Szmidt
  2024-01-25 16:13                                                 ` Eli Zaretskii
  0 siblings, 1 reply; 134+ messages in thread
From: Alfred M. Szmidt @ 2024-01-25 16:01 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: incal, emacs-devel

   > Who maintains a GNU package is maintained in
   > fp.gnu.org:/gd/gnuorg/maintainers.bypkg, that is the offical list of
   > GNU maintainers.

   That file is not accessible to general public, though.

There where multiple reasons for that, it contained quite a bit of
personal information.  But the individuals listed there are those who
maintain GNU Emacs, nothing else.  How then individuals GNU projects
distribute the load between maintainers is a different matter.





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

* Re: Code for cond*
  2024-01-25 15:44             ` Alfred M. Szmidt
  2024-01-25 16:00               ` JD Smith
@ 2024-01-25 16:05               ` Stefan Monnier
  2024-01-25 16:12                 ` Alfred M. Szmidt
  1 sibling, 1 reply; 134+ messages in thread
From: Stefan Monnier @ 2024-01-25 16:05 UTC (permalink / raw)
  To: Alfred M. Szmidt; +Cc: JD Smith, enometh, emacs-devel

>    (cond-let
>      (:let var-for-rest value)
>
> Is there a reason why using a keyword here?

It was to "ensure" compatibility with existing `cond` code.

Also it was based on the premise that most `cond`s would *not* use it
since such code is mostly needed to replace things like:

    (let (val)
      (cond
       [...]
       ((foo (setq var (bar)))
        [...])
       [...]))
    
And while such code does occur, it occurs only in a minority of `cond`
uses, so I thought it would be worthwhile for it to "stand out" so as
not to catch the reader by surprise.


        Stefan




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

* Re: Code for cond*
  2024-01-25 16:05               ` Stefan Monnier
@ 2024-01-25 16:12                 ` Alfred M. Szmidt
  2024-01-25 16:20                   ` Stefan Monnier
  2024-01-25 16:33                   ` JD Smith
  0 siblings, 2 replies; 134+ messages in thread
From: Alfred M. Szmidt @ 2024-01-25 16:12 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: jdtsmith, enometh, emacs-devel


   >    (cond-let
   >      (:let var-for-rest value)
   >
   > Is there a reason why using a keyword here?

   It was to "ensure" compatibility with existing `cond` code.

I really hope we are not going to modify cond -- which has been
literally the same since it created many decades ago.  It would be
confusing to all Lisp programmers, even if it was backward compatible.

   Also it was based on the premise that most `cond`s would *not* use it
   since such code is mostly needed to replace things like:

       (let (val)
	 (cond
	  [...]
	  ((foo (setq var (bar)))
	   [...])
	  [...]))

   And while such code does occur, it occurs only in a minority of `cond`
   uses, so I thought it would be worthwhile for it to "stand out" so as
   not to catch the reader by surprise.

Are such cases really worth solving by extending cond, and not better
addressed in a different macro (cond*, pcase, ...) -- or even just
sticking to the status quo?  

The above "idiom" is much easier to understand since it just uses well
known Lisp behaviour than modifying cond even if it is really ugly
... the feature is a nice addition, but is it worth it?



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

* Re: Code for cond*
  2024-01-25 16:01                                               ` Alfred M. Szmidt
@ 2024-01-25 16:13                                                 ` Eli Zaretskii
  0 siblings, 0 replies; 134+ messages in thread
From: Eli Zaretskii @ 2024-01-25 16:13 UTC (permalink / raw)
  To: Alfred M. Szmidt; +Cc: incal, emacs-devel

> From: "Alfred M. Szmidt" <ams@gnu.org>
> Cc: incal@dataswamp.org, emacs-devel@gnu.org
> Date: Thu, 25 Jan 2024 11:01:03 -0500
> 
>    > Who maintains a GNU package is maintained in
>    > fp.gnu.org:/gd/gnuorg/maintainers.bypkg, that is the offical list of
>    > GNU maintainers.
> 
>    That file is not accessible to general public, though.
> 
> There where multiple reasons for that, it contained quite a bit of
> personal information.  But the individuals listed there are those who
> maintain GNU Emacs, nothing else.

I'm not saying that file should be disclosed, I'm saying that the
public information about Emacs maintainers is in admin/MAINTAINERS.
And if the latter is inaccurate, we should fix that, because that's
the only way for people to know who maintains Emacs.



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

* Re: Code for cond*
  2024-01-25 16:12                 ` Alfred M. Szmidt
@ 2024-01-25 16:20                   ` Stefan Monnier
  2024-01-25 16:33                   ` JD Smith
  1 sibling, 0 replies; 134+ messages in thread
From: Stefan Monnier @ 2024-01-25 16:20 UTC (permalink / raw)
  To: Alfred M. Szmidt; +Cc: jdtsmith, enometh, emacs-devel

>    >    (cond-let
>    >      (:let var-for-rest value)
>    >
>    > Is there a reason why using a keyword here?
>
>    It was to "ensure" compatibility with existing `cond` code.
>
> I really hope we are not going to modify cond

Note how I used "was" above.  I'm just describing an idea I've had in
the past.  An idea I did not pursue.


        Stefan




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

* Re: Code for cond*
  2024-01-25 16:12                 ` Alfred M. Szmidt
  2024-01-25 16:20                   ` Stefan Monnier
@ 2024-01-25 16:33                   ` JD Smith
  1 sibling, 0 replies; 134+ messages in thread
From: JD Smith @ 2024-01-25 16:33 UTC (permalink / raw)
  To: Alfred M. Szmidt; +Cc: Stefan Monnier, Madhu, emacs-devel



> On Jan 25, 2024, at 11:12 AM, Alfred M. Szmidt <ams@gnu.org> wrote:
> 
> The above "idiom" is much easier to understand since it just uses well
> known Lisp behaviour than modifying cond even if it is really ugly
> ... the feature is a nice addition, but is it worth it?

I tend to agree that wrapping the entire cond in let is not too bad.  It increases indentation depth for a very common need, but has the advantage that you “look up to parent forms for the bindings” which I think is natural for most elisp developers.

There is however no equivalent simple idiom for cond-let — a proposed member of the if-let/when-let family.  Unless, that is, you consider this simple:

(catch 'cond-let
 (when simple0 (throw 'cond-let body0))
 (when-let (bindings1) (throw 'cond-let body1))
 (when-let (bindings2) (throw 'cond-let body2))
 (when-let (bindings3) (throw 'cond-let body3))
 (throw 'cond-let fallthrough-body))



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

* Re: Code for cond*
  2024-01-24 12:15         ` Alan Mackenzie
  2024-01-24 12:28           ` Emanuel Berg
  2024-01-25  9:10           ` Po Lu
@ 2024-01-25 23:32           ` Stefan Kangas
  2 siblings, 0 replies; 134+ messages in thread
From: Stefan Kangas @ 2024-01-25 23:32 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: rms, joaotavora, acorallo, emacs-devel, monnier, eliz

Alan Mackenzie <acm@muc.de> writes:

>> Now, the `pcase' macro has been with us for 14 years already, and we
>> have had plenty of time to learn its ins and outs.  It's heavily used in
>> many parts of Emacs, and many of us rely on it as a matter of routine.
>> Its success is not surprising, given that this style of pattern matching
>> is increasingly common in other programming languages.
>
> What is its "success"?  It is a failure, being accepted only by some
> Emacs hackers.

One data point is that 30% (27/90) of the packages I have installed here
use pcase.  Please have a look at GNU ELPA, NonGNU ELPA, and MELPA, if
you are so inclined, and let's not forget that it's used in core too.

This is of course not the only measure of success, but I think it says
something.

> It is difficult to learn, it may be easy to write, but is difficult to
> read, and difficult indeed to debug.

This is harder to measure objectively, but this discussion shows that
many ELisp hackers don't necessarily share that experience.

>> In summary, I recommend installing `cond*' as a new package on GNU ELPA.
>> This is a good way of exploring an alternative version of an existing
>> macro.
>
> That's just a way of ensuring it never comes to anything and just gets
> forgotten about.

Packages like `s', `dash', and many others that were never installed in
core, have become very popular, and not forgotten at all.  They have
even, either directly or indirectly, inspired some changes in core.
I think this has been overall very beneficial to the continued evolution
of Emacs.

So I happen to think that cond* would have every opportunity to become
successful if it was installed on GNU ELPA.

I'm currently discussing this issue with Eli, so I'll leave it there for
now, as I don't want to forego that discussion.  Rest assured that your
arguments have been heard, and are taken into account.



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

* Re: Code for cond*
  2024-01-23 18:10 ` Stefan Monnier via Emacs development discussions.
  2024-01-24  4:49   ` JD Smith
@ 2024-01-26  4:30   ` Richard Stallman
  2024-01-28  3:06     ` Stefan Monnier via Emacs development discussions.
  2024-01-26  4:30   ` Richard Stallman
  2 siblings, 1 reply; 134+ messages in thread
From: Richard Stallman @ 2024-01-26  4:30 UTC (permalink / raw)
  To: Stefan Monnier; +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. ]]]

  > >        ;; Execute FORM, and ignore its value
  > >        ;; (except if this is the last clause).
  > >        (FORM)

  > YAGNI.

If this were a special feature, it might not be worth having.

But it is not a special feature.  It is a special case of the general
rule that a clause with one element is a non-exit clause.

I think it will sometimes be useful to execute something unconditionally
in the middle of a cond*.  

  > >        ;; Variables to bind, as in let*, around the rest
  > >        ;; of the cond*.
  > >        ((bind* (x foobar) y z (foo 5) a))
  > >        ;; Bindings continue in effect for the whole cond* construct.

  > This is one of the functionality missing from `cond`, so I'm rather
  > favorable, but:
  > - why `bind*` when the rest of ELisp uses `let*` in the names of all
  >   related constructs?

`let*' is a Lisp function, and that is reflected in the syntax for using it.
Calling this `let*' would be misleading, because this is not a Lisp function
and can't have syntax similar to that of `let*'.  There is no way that
it could fit into cond* and have syntax like `let*'.

So better it have a different name.

  > - Why the double parens?

They make this a clause with only one element -- in other words, a
no-exit clause.

It would be possible to use the syntax

   (bind* BINDINGS...)

and say that `bind*' is a syntactic special case.
I think that having the no-exit rule is simpler overall.

However, I see no way to do this for `match*'.  It will be
used in both exiting and non-exit clauses.  I don't see
any other simple syntax to use to distinguish those two cases.

Do you have a suggestion?



  > - Why `*`?

I put * at the end of the names `bind*' and `match*' to go with
`cond*' and thus make it easier to recall how they go together.  Also
to make it clear that they are special syntax, not Lisp functions, so
their uses can't be understood like function calls.

To omit that * would be straightforward, but I think those reasons
are valid for keeping it.

  > - I'm not happy about using the same construct for "bindings
  >   for this clause" and "bindings for subsequent clauses".

The alternative to having a uniform rule to distinguish a non-exit
clause from an exiting clause is to specify four special symbols, one
for each combination (matching vs binding, one clause vs remaining
clauses), like this:

              MATCH     BIND

ONE CLAUSE    match*    bind*

ALL CLAUSES   match**   bind**

I am not wedded to those four names.

If people who like cond* would prefer it with this change, plesae let
me know.

-- 
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] 134+ messages in thread

* Re: Code for cond*
  2024-01-23 18:10 ` Stefan Monnier via Emacs development discussions.
  2024-01-24  4:49   ` JD Smith
  2024-01-26  4:30   ` Richard Stallman
@ 2024-01-26  4:30   ` Richard Stallman
  2024-01-28  4:16     ` Stefan Monnier via Emacs development discussions.
  2 siblings, 1 reply; 134+ messages in thread
From: Richard Stallman @ 2024-01-26  4:30 UTC (permalink / raw)
  To: Stefan Monnier; +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. ]]]

  > Finally, I see a fairly extensive but hardcoded pattern language, which
  > seems like a regression compared to Pcase where the pattern language is
  > built from a few primitives only (with the rest defined on top via
  > `pcase-defmacro`).

cond* also has a macro-like way to extend the patterns.

-- 
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] 134+ messages in thread

* Re: Code for cond*
  2024-01-24 19:12     ` Alan Mackenzie
@ 2024-01-27  3:35       ` Richard Stallman
  0 siblings, 0 replies; 134+ messages in thread
From: Richard Stallman @ 2024-01-27  3:35 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: incal, 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. ]]]

  > , in the generated code, the form (let* ,(cdr condition) ...) sometimes
  > gets evaluated twice.  Surely this is a Bad Thing.  Or am I missing
  > something?

Yes, that was a bug.  Thanks.

  > In cond*-match, you use gensyms.  I've not spent enough time
  > understanding the code, but would it be possible, somehow, to avoid
  > these?

I don't see a way to avoid using generated symbols.  (pcase uses them
too.)  There could be other ways to generate their names, but
what specific way would be bbetter?

-- 
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] 134+ messages in thread

* Re: Code for cond*
  2024-01-24 13:52                 ` João Távora
  2024-01-24 14:31                   ` Po Lu
@ 2024-01-27  3:35                   ` Richard Stallman
  2024-01-27  3:35                   ` Richard Stallman
  2 siblings, 0 replies; 134+ messages in thread
From: Richard Stallman @ 2024-01-27  3:35 UTC (permalink / raw)
  To: João Távora; +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. ]]]

  > that the new cond* fall-through bindings don't lexically wrap
  > their users.

I understand cond* in a different way which leads to the opposte conclusion.

match* is not a separate construct in its own right.  It is part of
the syntax of using cond*, and it can only be used there.

These bindings belong to cond*.  Their scope is a part of the cond*
expression, which does wrap their uses.

Would this be clearer if we replace `match*' with `:match'?  I
wouldn't mind minor changes like that.  I'm concerned about its
powerful features and its simplicity.

Another possible syntax change: non-exit clauses could also be mored
to top level, as in the first two lines in this example.

  (cond* :match (PATTERN DATUM)
         :bind (BINDINGS...)
         ;; The rest are exiting clauses.
         (:match (PATTERN DATUM) CLAUSE-BODY...)
         (COND-EXP CLAUSE-BODY...)
         (:bind (BINDINGS...) COND-EXP CLAUSE-BODY...))

I prefer the syntax I've already proposed, because it expresses
syntactix relationships using parens.  But I'll agree to changes like
these if others have a clear preference for them.

-- 
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] 134+ messages in thread

* Re: Code for cond*
  2024-01-24 13:52                 ` João Távora
  2024-01-24 14:31                   ` Po Lu
  2024-01-27  3:35                   ` Richard Stallman
@ 2024-01-27  3:35                   ` Richard Stallman
  2 siblings, 0 replies; 134+ messages in thread
From: Richard Stallman @ 2024-01-27  3:35 UTC (permalink / raw)
  To: João Távora; +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. ]]]

In pcase, all the patterns have to match one and the same datum.  I've
designed cond* so that each pattern-matching specifies a datum to
match the pattern to.  That is an advance.

But it could be convenient not to have to respecify the datum each time
when it is the same datum.

With the crrenbt cond* syntax, a way to say "use the same datum: could
be, by omitting the DATUM from the match* subform to reuee the datum
of the previous match* subform, like this:

  (cond* ((match* P1 foo) ...)
         ((match* PX) :no-exit)
         ((match* P2) ...)
         ((match* P3) ...)
         ((match* P4 bar) ...))

For the alternate keyword-based cond* synax, there could be a keyword
to specify a datum for subsequent matches.

  (cond* :datum foo
         ;; Next 4 match agains foo.
         (:match P1 ...)
         :match PX
         (:match P2 ...)
         (:match P3 ...)
         :datum bar
         (:match P4 ...))

I'm interested in feedback about this possible additional feature
in each of the two forms of syntax.



-- 
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] 134+ messages in thread

* Re: Code for cond*
  2024-01-26  4:30   ` Richard Stallman
@ 2024-01-28  3:06     ` Stefan Monnier via Emacs development discussions.
  2024-01-30  3:59       ` Richard Stallman
  0 siblings, 1 reply; 134+ messages in thread
From: Stefan Monnier via Emacs development discussions. @ 2024-01-28  3:06 UTC (permalink / raw)
  To: emacs-devel

>   > >        ;; Execute FORM, and ignore its value
>   > >        ;; (except if this is the last clause).
>   > >        (FORM)
>   > YAGNI.
>
> If this were a special feature, it might not be worth having.
>
> But it is not a special feature.  It is a special case of the general
> rule that a clause with one element is a non-exit clause.
>
> I think it will sometimes be useful to execute something unconditionally
> in the middle of a cond*.

I like the feature (especially since it's how we introduce bindings
that scope over the rest of the branches), but I find the syntax
too inconspicuous.

I'd rather either have some "magic keyword" indicate "this is not
a branch", or maybe use [...] instead of (...)?

>   > >        ;; Variables to bind, as in let*, around the rest
>   > >        ;; of the cond*.
>   > >        ((bind* (x foobar) y z (foo 5) a))
>   > >        ;; Bindings continue in effect for the whole cond* construct.
>
>   > This is one of the functionality missing from `cond`, so I'm rather
>   > favorable, but:
>   > - why `bind*` when the rest of ELisp uses `let*` in the names of all
>   >   related constructs?
>
> `let*' is a Lisp function, and that is reflected in the syntax for using it.
> Calling this `let*' would be misleading,

I didn't suggest to call it `let*`.
`pcase-let` is not called `let` and neither is `cl-macrolet` :-)

Having `let` in the name would make a lot more sense.

Also, if

    (FORM)

means "Execute FORM, and ignore its value", then the above

    ((bind* (x foobar) y z (foo 5) a))

would collide with an actual `bind*` function or macro.

That's also part of the reason why I considered using

    (:let ...)

when I considered adding bindings to `cond`.

>   > - Why `*`?
> I put * at the end of the names `bind*' and `match*' to go with
> `cond*' and thus make it easier to recall how they go together.  Also
> to make it clear that they are special syntax, not Lisp functions, so
> their uses can't be understood like function calls.

But there are macros and functions with `*` at the end of their names,
so it doesn't make it clear to me at all.

In my view of the Lisp world, when you talk about symbols that represent
special syntax that are only given a meaning by where they're used,
I think of keywords instead (hence my use of `:let`), which cannot be
used as variables and by convention are never used as functions either.

>   > - I'm not happy about using the same construct for "bindings
>   >   for this clause" and "bindings for subsequent clauses".
> The alternative to having a uniform rule to distinguish a non-exit

I do like such a uniform rule.  I just think it needs to be more visible
than just the absence of something before the next close paren.


        Stefan




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

* Re: Code for cond*
  2024-01-26  4:30   ` Richard Stallman
@ 2024-01-28  4:16     ` Stefan Monnier via Emacs development discussions.
  2024-01-31  3:32       ` Richard Stallman
  0 siblings, 1 reply; 134+ messages in thread
From: Stefan Monnier via Emacs development discussions. @ 2024-01-28  4:16 UTC (permalink / raw)
  To: emacs-devel

Richard Stallman [2024-01-25 23:30:24] wrote:

> [[[ 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. ]]]
>
>   > Finally, I see a fairly extensive but hardcoded pattern language, which
>   > seems like a regression compared to Pcase where the pattern language is
>   > built from a few primitives only (with the rest defined on top via
>   > `pcase-defmacro`).
>
> cond* also has a macro-like way to extend the patterns.

Is this what you're referring to?

        ((get (car subpat) 'cond*-expander)
         ;; Treat result as a subpattern.
         (cond*-subpat (funcall (get (car subpat) 'cond*-expander) subpat)
                       cdr-safe bindings backtrack-aliases data))

do we have reasons to believe that it's flexible enough?
For `pcase`, I needed to add the `app` pattern before `pcase-defmacro`
was actually usable for more realistic extensions.  And to be sure it
was flexible enough I restructured the code so that patterns like
backquote are not hardcoded in Pcase but are implemented using this
extension facility.

But more importantly, I really fail to see why we can't just use Pcase
patterns here (potentially with adjustments to Pcase)?

Let's look at them one by one:

    CONSTANT: nil, t, a keyword, a quoted constant,
    VARIABLE: a symbol
    A backquoted cons cell
    (or SUBPATTERNS...)
    (and SUBPATTERNS...)
    (rx RX-PATTERN)

these are identical to Pcase (well, maybe not 100%, since that depends on
details of the implementation which I haven't been able to test yet,
but to a large extent at least).

    MACRO-CALL: a list which is a call to a macro.

I used that trick in `gv.el` for places and it served us well, but I'm
wondering if it'll work as well here.  Can you think of macros for which
this will do something useful?  If it's rarely useful, then we have to
take into account the potential drawback of having "mysterious" behavior
or errors when the programmer didn't expect this macro expansion.

    A vector

Pcase currently doesn't support exactly this, but does support it within
the backquote pattern, i.e. instead of

    [PAT1 PAT2 ...]

you have to use

    `[,PAT1 ,PAT2 ...]

and with the other difference that it only matches if the two vectors
have the same length.  I resisted the addition of a vector pattern
because I didn't know which kinds of patterns would be most useful
(e.g. in terms of how the length should be constrained, ...).

We could add a pattern like the one you have in `match*` to Pcase, but
the experience with Pcase suggests that patterns like that on vectors
are *very* rarely use(d|ful), so I don't think it's worth the trouble.

    (cdr-safe BACKQUOTED-CONS-CELL)
    (cdr BACKQUOTED-CONS-CELL)

These would be easy to add to Pcase, of course.

    A string

In Pcase patterns, strings are considered to fall within the first case
above, i.e. constants.  We have `re-match` of course, when we need to
match a regex against a string.  Which one deserves the shorter syntax of
"just a string" is debatable (of course, some readers may point out
that the question would be moot if we had a special syntax for
regexps).

    (rx RX-PATTERN VARS...)

For some reason currently our `rx` doesn't have such an option, but it
would be easy to add, AFAICT.

    A backquoted structure constructor

We don't currently support hat in Pcase, but neither does your code, and
Pcase could support it just as well.

    (PRED VARIABLE)  or, more generally,

We don't have that currently.  Instead we have

    (pred PRED)

and if you want to bind it to a var you need

    (and (pred PRED) VAR)

Extending `pred` so it can bind the value to a variable is definitely an
option.  It's fairly often useful.  That's one of the reasons why I was
careful to limit `pred` to a single argument so far (so it has room to
grow by giving some useful meaning to additional args based on experience).

E.g. we could easily add support for

    (pred PRED VARIABLE)

And of course we could also add support for your (PRED VARIABLE) syntax,
although until now I've resisted the urge to eat up Pcase pattern's
namespace this way.

    (PRED VARIABLE OTHER-ARGS...)

Without the VARIABLE part, in Pcase this is almost the same as

    (pred (PRED OTHER-ARGS...))

except that in Pcase, the value is passed as the last argument rather as
first argument.  I haven't seen a strong reason to prefer one over the
other.  I've considered extending `pred` to support the use of `_` in
OTHER-ARGS... in order to specify the argument position of the value.
If we combine that with the above (pred PRED VAR) extension, then we
could get the same behavior as (PRED VARIABLE OTHER-ARGS...) with

    (pred (PRED _ OTHER-ARGS...) VARIABLE)

and finally:

    (constrain VAR EXPRESSION)

This would be very easy to add.  It's basically

    (and VAR (guard EXPRESSION))

and just as for `pred` there is evidence that it would be desirable to
extend it so as to include the `and` behavior, so we could have

    (guard VAR EXPRESSION)

IOW, while your patterns aren't 100% covered by current Pcase patterns,
Pcase can easily accommodate them, and it would be a shame to introduce
a second pattern language that's almost-identical-but-not-quite.

Is there *any* reason why `cond*` needs a different pattern language?


        Stefan


PS: Is the code available for testing somewhere?  The code I have here
(from Jan 19) doesn't work:

    ELISP> (cond* ((match* `(a ,y) '(a 5)) y))
    *** Eval error ***  Symbol’s value as variable is void: ba162
    ELISP>




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

* Re: Code for cond* - cond*-match, cond*-subpat and backtrack-aliases
  2024-01-25 14:01   ` Code for cond* - cond*-match, cond*-subpat and backtrack-aliases Alan Mackenzie
@ 2024-01-29  3:19     ` Richard Stallman
  2024-01-29  8:54       ` Andreas Schwab
  2024-01-29  3:19     ` Richard Stallman
  1 sibling, 1 reply; 134+ messages in thread
From: Richard Stallman @ 2024-01-29  3:19 UTC (permalink / raw)
  To: Alan Mackenzie; +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. ]]]

  > 4/- There are several place where "\\>" is appended to a regexp.  Would
  >   "\\_>" ("end of symbol"), which we've had in Emacs for 10 or 15 years
  >   now, perhaps be better?

/> in a regexp means "end of the text to be matched".  The result of
that, in string-match, is that it has to match the whole of the datum.
That's the behavior I had in mind.

-- 
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] 134+ messages in thread

* Re: Code for cond* - cond*-match, cond*-subpat and backtrack-aliases
  2024-01-25 14:01   ` Code for cond* - cond*-match, cond*-subpat and backtrack-aliases Alan Mackenzie
  2024-01-29  3:19     ` Richard Stallman
@ 2024-01-29  3:19     ` Richard Stallman
  2024-01-29 12:16       ` JD Smith
  1 sibling, 1 reply; 134+ messages in thread
From: Richard Stallman @ 2024-01-29  3:19 UTC (permalink / raw)
  To: Alan Mackenzie; +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. ]]]

There were a number of bugs in the last version I sent out.  Thanks
for investigating them.  I had to work on other things, but now I
believe this code mostly works.  One of those typos was still present,
so I fixed it -- thanks.

Here is the new version.  I made a test framework for it
and have used that to verify a number of test cases.
There is one bug that I can't localize: the byue compiler reports `.' is
called as a function.  But I can't see where.

;;; -*-lexical-binding: t; -*-
;;; subpat compilation should have some list structure from containing exp
;;; to pass to byte-compile-warn-x to find the right place in source code.

;; Copyright (C) 1985-2024 Free Software Foundation, Inc.

;; Maintainer: rms@gnu.org
;; Package: emacs

;; This file is cond*,  not yet part of GNU Emacs.

;; cond* is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; cond* is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.

(defmacro cond* (&rest clauses)
  "Extended form of traditional Lisp `cond' construct.
A `ond*' construct is a series of clauses, and a clause
normally has the form (CONDITION BDOY...).

CONDITION can be a Lisp expression, as in `cond'.
Or it can be `(bind* BINDINGS...)' or `(match* PATTERN DATUM)'.

`(bind* BINDINGS...)' means to bind BINDINGS (as if they were in `let*')
for the body of the clause.  As a condition, it counts as true
if the first binding's value is non-nil.  All the bindings are made
unconditionally for whatever scope they cover.

`(match* PATTERN DATUM)' means to match DATUM against the pattern PATTERN
The condition counts as teue if PATTERN matches DATUM.

Mon=exit clause:

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

\\[match*\\] for documention of the patterns for use in `match*'."
  (cond*-convert clauses))

(defmacro match* (pattern datum)
  "This specifies matching DATUM against PATTERN.
It is not really a LIsp function, and it is meaningful
only in the CONDITION of a `cond*' clause.

`_' matvhes any value.
KEYWORD matches that keyword.
nil  matches nil.
t    matches t.
SYMBOL matches any value and binds SYMBOL to that value.
  If SYMBOL has been matched and bound earlier in this pattern,
  it matches here the same value that it mached before.
REGEXP matches a string if REGEXP matches it.
  The match must cover the entire string from its first char to its last.
ATOM (meaning any other kind of non-list not described above)
  matches anything `equal' to it.
(rx REGEXP) uses a regexp specified in s-expression form,
  as in the function `rx', and matches the data that way.
(rx REGEXP SYM0 SYM1...) uses a regexp specified in s-expression form,
  and binds the symbols SYM0, SYM1, and so on
  to (match-string 0 DATUN), (match-string 1 DATUM), and so on.
  You can use as many SYMs as regexp matching supports.

`OBJECT  matches any value `equal' to OBJECT.
(cons CARPAT CDRPAT)
  matches a cons cell if CARPAT matches its car and CDRPAT matches its cdr.
(list ELTPATS...)
  matches a list if the ELTPATS match its elements.
  The first ELTPAT should match the list's first element.
  The second ELTPAT should match the list's second element.  And so on.
(vector ELTPATS...)
  matches a vector if the ELTPATS match its elements.
  The first ELTPAT should match the vector's first element.
  The second ELTPAT should match the vector's second element.  And so on.
(cdr PATTERN)  matches PATTERN with strict checking of cdrs.
  That means that `list' patterns verify that the final cdr is nil.
  Strict checking is the default.
(cdr-safe PATTERN)  matches PATTERN with lax checking of cdrs.
  That means that `list' patterns do not examine the final cdr.
(and CONJUNCTS...)  matches each of te CONJUNCTS against the same data.
  If all of them match, this pattern succeeds.
  If one CONJUNCT fails, this pattern fails and does not try more CONJUNCTS.
(or DISJUNCTS...)  matches each of te DISJUNCTS against the same data.
  If all of them match, this pattern matches.
  If one DISJUNCT succeeds, this pattern succeeds
  and does not try more DISJUNCT.
(COND*-EXPENDER ...)
(PREDICATE SYMBOL)
  matches datum if (PREDICATE DATUM) is true,
  then binds SYMBOL to DATUM.
(PREDICATE SYMBOL MORE-ARGS...)
  matches datum if (PREDICATE DATUM MORE-ARGS...) is true,
  then binds SYMBOL to DATUM.
  MORE-ARGS... can refer to symbols bound earlier in the pattern.
(constrain SYMBOL EXP)
  matches datum if the form EXP is true.
  EXP can refer to symbols bound earlier in the pattern."
  (byte-compile-warn-x pattern "`match*' used other than as a `cond*' condition"))

(defun cond*-non-exit-clause-p (clause)
  "If CLAUSE, a cond* clause, is a non-exit clause, return t."
  (or (null (cdr-safe clause))   ;; clause has only one element.
      (and (cdr-safe clause)
           ;; Starts with t.
           (or (eq (car clause) t)
               ;; Begins with keyword.
               (keywordp (car clause))))
      ;; Ends with keyword.
      (keywordp (car (last clause)))))

(defun cond*-non-exit-clause-substance (clause)
  "For a non-exit cond* clause CLAUSE, return its substance.
This removes a final keyword if that's what makes CLAUSE non-exit."
  (cond ((null (cdr-safe clause))   ;; clause has only one element.
         clause)
        ;; Starts with t or a keyword.
        ;; Include t as the first element of the substancea
        ;; so that the following element is not treated as a pattern.
        ((and (cdr-safe clause)
              (or (eq (car clause) t)
                  (keywordp (car clause))))
         ;; Standardize on t as the first element.
         (cons t (cdr clause)))

        ;; Ends with keyword.
        ((keywordp (car (last clause)))
         ;; Do NOT include the final keyword.
         (butlast clause))))

(defun cond*-convert (clauses)
  "Process a list of cond* clauses, CLAUSES.
Returns the equivalent Lisp expression."
  (if clauses
      (cond*-convert-clause (car-safe clauses) (cdr-safe clauses))))

(defun cond*-convert-clause (clause rest)
  "Process one `cond*' clause, CLAUSE.
REST is the rest of the clauses of this cond* expression."
  (if (cond*-non-exit-clause-p clause)
      ;; Handle a non-exit clause.  Make its bindings active
      ;; around the whole rest of this cond*, treating it as
      ;; a condition whose value is always t, around the rest
      ;; of this cond*.
      (let ((substance (cond*-non-exit-clause-substance clause)))
        (cond*-convert-condition
         ;; Handle the first substantial element in the non-exit clause
         ;; as a matching condition.
         (car substance)
         ;; Any following elements in the
         ;; non-exit clause are just expressions.
         (cdr substance)
         ;; Remaining clauses will be UNCONDIT-CLAUSES:
         ;; run unconditionally and handled as a cond* body.
         rest
         nil nil))
    ;; Handle a normal (conditional exit) clauss.
    (cond*-convert-condition (car-safe clause) (cdr-safe clause) nil
                             rest (cond*-convert rest))))

(defun cond*-convert-condition (condition true-exps uncondit-clauses rest iffalse)
  "Process the condition part of one cond* clause.
TRUE-EXPS is a list of Lisp expressions to be executed if this
condition is true, and inside its bindings.
UNCONDIT-CLAUSES is a list of cond*-clauses to be executed if this
condition is true, and inside its bindings.
This is used for non-exit clauses; it is nil for conditional-exit clauses.

REST and IFFALSE are non-nil for conditional-exit clauses that are not final.
REST is a list of clauses to process after this one if
this one could have exited but does not exit.
This is used for conditional exit clauses.
IFFALSE is the value to compute after this one if
this one could have exited but does not exit.
This is used for conditional exit clauses."
  (if (and uncondit-clauses rest)
      (error "Clase is both exiting and non-exit"))
  (let ((pat-type (car-safe condition)))
    (cond ((eq pat-type 'bind*)
           (let* ((bindings (cdr condition))
                  (first-binding (car bindings))
                  (first-variable (if (symbolp first-binding) first-binding
                                   (car first-binding)))
                  (first-value (if (symbolp first-binding) nil
                                 (cadr first-binding)))
                  (init-gensym (gensym "init"))
                  ;; BINDINGS with the initial value of the first binding
                  ;; replaced by INIT-GENSYM.
                  (mod-bindings
                   (cons (list first-variable init-gensym) (cdr bindings))))
             ;;; ??? Here pull out all nontrivial initial values
             ;;; ??? to compute them earlier.
             (if rest
                 ;; bind* starts an exiting clause which is not final.
                 ;; Therefore, must run IFFALSE.
                 `(let ((,init-gensym ,first-value))
                    (if ,init-gensym
                        (let* ,mod-bindings
                          . ,true-exps)
                      ;; Always calculate all bindings' initial values,
                      ;; but the bindings must not cover IFFALSE.
                      (let* ,mod-bindings nil)
                      ,iffalse))
               (if uncondit-clauses
                   ;; bind* starts a non-exit clause which is not final.
                   ;; Run the TRUE-EXPS if condition value is true.
                   ;; Then always go on to run the UNCONDIT-CLAUSES.
                   (if true-exps
                       `(let ((,init-gensym ,first-value))
;;; ??? Should we make the bindings a second time for the UNCONDIT-CLAUSES.
;;; as the doc string says, for uniformity with match*?
                          (let* ,mod-bindings
                            (when ,init-gensym
                              . ,true-exps)
                            ,(cond*-convert uncondit-clauses)))
                     `(let* ,bindings
                        ,(cond*-convert uncondit-clauses)))
                 ;; bind* starts a final clause.
                 ;; If there are TRUE-EXPS, run them if condition succeeded.
                 ;; Always make the bindings, in case the
                 ;; initial values have side effects.
                 `(let ((,init-gensym ,first-value))
                    ;; Calculate all binding values unconditionally.
                    (let* ,mod-bindings
                      (when ,init-gensym
                        . ,true-exps)))))))
          ((eq pat-type 'match*)
           (cond*-match condition true-exps uncondit-clauses iffalse))
          (t
           ;; Ordinary Lixp expression is the condition 
           (if rest
               ;; A nonfinal exiting clause.
               ;; If condition succeeds, run the TRUE-EXPS.
               ;; There are following clauses, so run IFFALSE
               ;; if the condition fails.
               `(if ,condition
                    (progn . ,true-exps)
                  ,iffalse)
             (if uncondit-clauses
                 ;; A non-exit clause.
                 ;; If condition succeeds, run the TRUE-EXPS.
                 ;; Then always go on to run the UNCONDIT-CLAUSES.
                 `(progn (if ,condition
                             (progn . ,true-exps))
                         ,(cond*-convert uncondit-clauses))
               ;; An exiting clause which is also final.
               ;; If there are TRUE-EXPS, run them if CONDITION succeeds.
               (if true-exps
                   `(if ,condition (progn . ,true-exps))
                 ;; Run and return CONDITION.
                 condition)))))))
\f
(defun cond*-match (matchexp true-exps uncondit-clauses iffalse)
  "Generate code to match a match* pattern PATTERN.
Match it against data represented by the expression DATA.
TRUE-EXPS, UNCONDIT-CLAUSES and IFFALSE have the same meanings
as in `cond*-condition'."
  (when (or (null matchexp) (null (cdr-safe matchexp))
            (null (cdr-safe (cdr matchexp)))
            (cdr-safe (cdr (cdr matchexp))))
    (byte-compile-warn-x matchexp "Malformed (match* ...) expression"))
  (let* (raw-result
         (pattern (nth 1 matchexp))
         (data (nth 2 matchexp))
         expression
         (inner-data data)
         ;; Add backtrack aliases for or-subpatterns to cdr of this.
         (backtrack-aliases (list nil))
         run-true-exps
         gensym)
    ;; For now, always bind a gensym to the data to be matched.
    (setq gensym (gensym "d") inner-data gensym)
    ;; Process the whole pattern as a subpattern.
    (setq raw-result (cond*-subpat pattern nil nil nil backtrack-aliases inner-data))
    (setq expression (cdr raw-result))
    ;; Make an expression to run the TRUE-EXPS inside our bindings.
    (setq run-true-exps
          (cond*-bind-pattern-syms
           (car raw-result)
           `(progn . ,true-exps)))
    ;; Run TRUE-EXPS if match succeeded.  Bind our bindings around it.
    (setq expression
          (if (and (null run-true-exps) (null iffalse))
              ;; We MUST compute the expression, even when no decision
              ;; depends on its value, because it may call functions with
              ;; side effects.
              expression
            `(if ,expression
                 ,run-true-exps
               ;; For a non-final exiting clause, run IFFALSE if match failed.
               ;; Don't bind the bindings around it, since
               ;; an exiting clause's bindings don't affect later clauses.
               ,iffalse)))
    ;; For a non-final non-exiting clause,
    ;; always run the UNCONDIT-CLAUSES.
    (if uncondit-clauses
        (setq expression
              `(progn ,expression 
                      ,(cond*-bind-pattern-syms
                        (car raw-result)
                        (cond*-convert uncondit-clauses)))))
    ;; Bind the backtrack-aliases if any.
    ;; We need them bound for the TRUE-EXPS.
    ;; It is harmless to bind them around IFFALSE
    ;; because they are all gensyms anyway.
    (if (cdr backtrack-aliases)
        (setq expression
              `(let ,(mapcar 'cdr (cdr backtrack-aliases))
                 ,expression)))
    ;; If we used a gensym, wrap on code to bind it.
    (if gensym
        (if (and (listp expression) (eq (car expression) 'progn))
            `(let ((,gensym ,data)) . ,(cdr expression))
          `(let ((,gensym ,data)) ,expression))
      expression)))

(defun cond*-bind-pattern-syms (bindings expr)
  "Wrap EXPR in code to bind the BINDINGS.
This is used for the bindings specified explicitly in match* patterns."
  ;; They can't have side effects.   Skip them
  ;; if we don't actually need them.
  (if (equal expr '(progn))
      nil
    (if bindings
        (if (eq (car expr) 'progn)
            `(let* ,bindings . ,(cdr expr))
          `(let* ,bindings ,expr))
      expr)))

(defvar cond*-debug-pattern nil)

;;; ??? Structure type patterns not implemented yet.
;;; ??? Probably should optimize the `nth' calls in handling `list'.

(defun cond*-subpat (subpat cdr-ignore bindings inside-or backtrack-aliases data)
  "Generate code to match ibe subpattern within `match*'.
SUBPAT is the subpattern to handle.
CDR-IGNORE if true means don't verify there are no extra elts in a list.
BINDINGS is the list of bindings made by
the containing and previous subpatterns of this pattern.
Each element of BINDINGS must have the frm (VAR VALUE).
BACKTRACK-ALIASES is used to pass data upward.  Initial call should
pass (list).  The cdr of this collects backtracking aliases made for
variables boung within (or...) patterns so that the caller
can bind them etc.  Each of them has the form (USER-SYMBOL . GENSYM).
DATA is the expression for the data that this subpattern is
supposed to match against.

Return Value has the form (BINDINGS . CONDITION), where
BINDINGS is the list of bindings to be made for SUBPAT
plus the subpatterns that contain/precede it.
Each element of BINDINGS has the form (VAR VALUE).
CONDITION is the condition to be tested to decide
whether SUBPAT (as well as the subpatterns that contain/precede it) matches,"
  (if (equal cond*-debug-pattern subpat)
      (debug))
;;;  (push subpat subpat-log)
  (cond ((eq subpat '_)
         ;; _ as pattern makes no bindings and matches any data.
         (cons bindings t))
        ((memq subpat '(nil t))
         (cons bindings `(eq ,subpat ,data)))
        ((keywordp subpat)
         (cons bindings `(eq ,subpat ,data)))
        ((symbolp subpat)
         (let ((this-binding (assq subpat bindings))
               (this-alias (assq subpat (cdr backtrack-aliases))))
           (if this-binding
               ;; Variable already bound.
               ;; Compare what this variable should be bound to
               ;; to the data it is supposed to match.
               ;; That is because we don't actually bind these bindings
               ;; around the condition-testing expression.
               (cons bindings `(equal ,(cadr this-binding) ,data))
             (if inside-or
                 (let (alias-gensym)
                   (if this-alias
                       ;; Inside `or' subpattern, if this symbol already 
                       ;; has an alias for backtracking, just use that.
                       ;; This means the symbol was matched
                       ;; in a previous arm of the `or'.
                       (setq alias-gensym (cdr this-alias))
                     ;; Inside `or' subpattern but this symbol has no alias,
                     ;; make an alias for it.
                     (setq alias-gensym (gensym "ba"))
                     (push (cons subpat alias-gensym) (cdr backtrack-aliases)))
                   ;; Make a binding for the symbol, to its backtrack-alias,
                   ;; and set the alias (a gensym) to nil.
                   (cons `((,subpat ,alias-gensym) . ,bindings)
                         `(setq ,alias-gensym ,data)))
               ;; Not inside `or' subpattern: ask for a binding for this symbol
               ;; and say it does match whatever datum.
               (cons `((,subpat ,data) . ,bindings)
                     t)))))
        ;; Various constants.
        ((numberp subpat)
         (cons bindings `(eql ,subpat ,data)))
        ;; Regular expressions as strings.
        ((stringp subpat)
         (cons bindings `(string-match ,(concat subpat "\\>") ,data)))
        ;; All other atoms match with `equal'.
        ((not (consp subpat))
         (cons bindings `(equal ,subpat ,data)))
        ((not (consp (cdr subpat)))
         (byte-compile-warn-x subpat "%s subpattern with malformed or missing arguments" (car subpat)))
        ;; Regular expressions specified as list structure.
        ;; (rx REGEXP VARS...)
        ((eq (car subpat) 'rx)
         (let* ((rxpat (concat (rx-to-string (cadr subpat) t) "\\>"))
                (vars (cddr subpat)) setqs (varnum 0)
                (match-exp `(string-match ,rxpat ,data)))
           (if (null vars)
               (cons bindings match-exp)
             ;; There are variables to bind to the matched substrings.
             (if (> (length vars) 10)
                 (byte-compile-warn-x vars "Too many variables specified for matched substrings"))
             (dolist (elt vars)
               (unless (symbolp elt)
                 (byte-compile-warn-x vars "Non-symbol %s given as name for matched substring" elt)))
             ;; Bind these variables to nil, before the pattern.
             (setq bindings (nconc (mapcar 'list vars) bindings))
             ;; Make the expressions to set the variables.
             (setq setqs (mapcar
                          (lambda (var)
                            (prog1 `(setq ,var (match-string ,varnum ,data))
                              (setq varnum (1+ varnum))))
                          vars))
             (cons bindings `(if ,match-exp
                                 (progn ,@setqs t))))))
        ;; Quoted object as constant to match with `eq' or `equal'.
        ((eq (car subpat) 'quote)
         (if (symbolp (car-safe (cdr-safe subpat)))
             (cons bindings `(eq ,subpat ,data))
           (cons bindings `(equal ,subpat ,data))))
        ;; Match a call to `cons' by destructuring.
        ((eq (car subpat) 'cons)
         (let (car-result cdr-result car-exp cdr-exp)
           (setq car-result
                 (cond*-subpat (nth 1 subpat) cdr-ignore bindings inside-or backtrack-aliases `(car ,data)))
           (setq bindings (car car-result)
                 car-exp (cdr car-result))
           (setq cdr-result
                 (cond*-subpat (nth 2 subpat) cdr-ignore bindings inside-or backtrack-aliases `(cdr ,data)))
           (setq bindings (car cdr-result)
                 cdr-exp (cdr cdr-result))
           (cons bindings
                 (cond*-and `((consp ,data) ,car-exp ,cdr-exp)))))
        ;; Match a call to `list' by destructuring.
        ((eq (car subpat) 'list)
         (let ((i 0) expressions)
           ;; Check for bad structure of SUBPAT here?
           (dolist (this-elt (cdr subpat))
             (let ((result 
                    (cond*-subpat this-elt cdr-ignore bindings inside-or backtrack-aliases `(nth ,i ,data))))
               (setq bindings (car result))
               (push `(consp ,(if (zerop i) data `(nthcdr ,i ,data)))
                     expressions)
               (setq i (1+ i))
               (push (cdr result) expressions)))
           ;; Verify that list ends here, if we are suppose to check that.
           (unless cdr-ignore
             (push `(null (nthcdr ,i ,data)) expressions))
           (cons bindings (cond*-and (nreverse expressions)))))
        ;; Match (apply 'vector (backquote-list* LIST...)), destructuring.
        ((eq (car subpat) 'apply)
         ;; We only try to handle the case generated by backquote.
         ;; Convert it to a call to `vector' and handle that.
         (let ((cleaned-up
                `(vector . ,(cond*-un-backquote-list* (cdr (nth 2 subpat))))))
           ;; (cdr (nth 2 subpat)) gets LIST as above.
           (cond*-subpat cleaned-up
                         cdr-ignore bindings inside-or backtrack-aliases data)))
        ;; Match a call to `vector' by destructuring.
        ((eq (car subpat) 'vector)
         (let* ((elts (cdr subpat))
                (length (length elts))
                expressions (i 0))
           (dolist (elt elts)
             (let* ((result 
                     (cond*-subpat elt cdr-ignore
                                   bindings inside-or backtrack-aliases `(aref ,i ,data))))
               (setq i (1+ i))
               (setq bindings (car result))
               (push (cdr result) expressions)))
           (cons bindings
                 (cond*-and `((vectorp ,data) (= (length ,data) ,length)
                              . , (nreverse expressions))))))
        ;; Subpattern to set the cdr-ignore flag
        ((eq (car subpat) 'cdr-ignore)
         (cond*-subpat (cadr subpat) t bindings inside-or backtrack-aliases data))
        ;; Subpattern to clear the cdr-ignore flag
        ((eq (car subpat) 'cdr)
         (cond*-subpat (cadr subpat) nil bindings inside-or backtrack-aliases data))
        ;; Handle conjunction subpatterns.
        ((eq (car subpat) 'and)
         (let (expressions)
           ;; Check for bad structure of SUBPAT here?
           (dolist (this-elt (cdr subpat))
             (let ((result 
                    (cond*-subpat this-elt cdr-ignore bindings inside-or backtrack-aliases data)))
               (setq bindings (car result))
               (push (cdr result) expressions)))
           (cons bindings (cond*-and ,(nreverse expressions)))))
        ;; Handle disjunction subpatterns.
        ((eq (car subpat) 'or)
         ;; The main complexity is unsetting the pattern variables
         ;; that tentatively matches in an or-branch  that later failed.
         (let (expressions
               (bindings-before-or bindings)
               (aliases-before-or (cdr backtrack-aliases)))
           ;; Check for bad structure of SUBPAT here?
           (dolist (this-elt (cdr subpat))
             (let* ((bindings bindings-before-or)
                    bindings-to-clear expression
                    result)
               (setq result 
                     (cond*-subpat this-elt cdr-ignore bindings t backtrack-aliases data))
               (setq bindings (car result))
               (setq expression (cdr result))
               ;; Were any bindings made by this arm of the disjunction?
               (when (not (eq bindings bindings-before-or))
                 ;; Ok, arrange to clear their backtrack aliases
                 ;; if this arm does not match.
                 (setq bindings-to-clear bindings)
                 (let (clearing)
                   ;; For each of those bindings,
                   (while (not (eq bindings-to-clear bindings-before-or))
                     ;; Make an expression to set it to nil, in CLEARING.
                     (let* ((this-variable (caar bindings-to-clear))
                            (this-backtrack (assq this-variable
                                                  (cdr backtrack-aliases))))
                       (push `(setq ,(cdr this-backtrack) nil) clearing))
                     (setq bindings-to-clear (cdr bindings-to-clear)))
                   ;; Wrap EXPRESSION to clear those backtrack aliases
                   ;; if EXPRESSION is false.
                   (setq expression
                         (if (null clearing)
                             expression
                           (if (null (cdr clearing))
                               `(or ,expression
                                    ,(car clearing))
                             `(progn ,@clearing))))))
               (push expression expressions)))
           ;; At end of (or...), EACH variable bound by any arm
           ;; has a backtrack alias gensym.  At run time, that gensym's value
           ;; will be what was bound in the successful arm, or nil.
           ;; Now make a binding for each variable from its alias gensym.
           (let ((aliases (cdr backtrack-aliases)))
             (while (not (eq aliases aliases-before-or))
               (push `(,(caar aliases) ,(cdar aliases)) bindings)
               (pop aliases)))
           (cons bindings `(or . ,(nreverse expressions)))))
        ;; Expand cond*-macro call, treat result as a subpattern.
        ((get (car subpat) 'cond*-expander)
         ;; Treat result as a subpattern.
         (cond*-subpat (funcall (get (car subpat) 'cond*-expander) subpat)
                       cdr-ignore bindings inside-or backtrack-aliases data))
        ((macrop (car subpat))
         (cond*-subpat (macroexpand subpat) cdr-ignore bindings inside-or backtrack-aliases data))
        ;; Simple constrained variable, as in (symbolp x).
        ((functionp (car subpat))
         ;; Without this, nested constrained variables just work.
         (unless (symbolp (cadr subpat))
           (byte-compile-warn-x subpat "Complex pattern nested in constrained variable pattern"))
         (let* ((rest-args (cddr subpat))
                ;; Process VAR to get a binding for it.
                (result (cond*-subpat (cadr subpat) cdr-ignore bindings inside-or backtrack-aliases data))
                (new-bindings (car result))
                (expression (cdr result))
                (combined-exp
                 (cond*-and (list `(,(car subpat) ,data . ,rest-args) expression))))

           (cons new-bindings
                 (cond*-bind-around new-bindings combined-exp))))
        ;; Generalized constrained variable: (constrain VAR EXP)
        ((eq (car subpat) 'constrain)
         ;; Without this, nested constrained variables just work.
         (unless (symbolp (cadr subpat))
           (byte-compile-warn-x subpat "Complex pattern nested in constrained variable pattern"))
         ;; Process VAR to get a binding for it.
         (let ((result (cond*-subpat (cadr subpat) cdr-ignore bindings inside-or backtrack-aliases data)))
           (cons (car result)
                 ;; This is the test condition 
                 (cond*-bind-around (car result) (nth 2 subpat)))))
        (t 
         (byte-compile-warn-x subpat "Undefined pattern type `%s' in `cond*'" (car subpat)))))

;;; Subroutines of cond*-subpat.

(defun cond*-bind-around (bindings exp)
  "Wrap a `let*' around EXP, to bind those of BINDINGS used in EXP."
  (let ((what-to-bind (cond*-used-within bindings exp)))
    (if what-to-bind
        `(let* ,(nreverse what-to-bind) ,exp)
      exp)))

(defun cond*-used-within (bindings exp)
  "Return the list of those bindings in BINDINGS which EXP refers to.
This operates naively and errs on the side of overinclusion,
and does not distinguish function names from variable names.
That is safe for the purpose this is used for."
  (cond ((symbolp exp) 
         (let ((which (assq exp bindings)))
           (if which (list which))))
        ((listp exp)
         (let (combined (rest exp))
           ;; Find the bindings used in each element of EXP
           ;; and merge them together in COMBINED.
           ;; It would be simpler to use dolist at each level,
           ;; but this avoids errors from improper lists.
           (while rest
             (let ((in-this-elt (cond*-used-within bindings (car rest))))
               (while in-this-elt
                 ;; Don't insert the same binding twice.
                 (unless (memq (car-safe in-this-elt) combined)
                   (push (car-safe in-this-elt) combined))
                 (pop in-this-elt)))
             (pop rest))
           combined))))

;; Construct a simplified equivalent to `(and . ,CONJUNCTS),
;; assuming that it will be used only as a truth value.
;; We don't bother checking for nil in CONJUNCTS
;; because that would not normally happen.
(defun cond*-and (conjuncts)
  (setq conjuncts (remq t conjuncts))
  (if (null conjuncts)
      t
    (if (null (cdr conjuncts))
        (car conjuncts)
      `(and . ,conjuncts))))

;; Convert the arguments in a form that calls `backuotelist*'
;; into equivalent args to pass to `list'.
;; We assume the last argument has the form 'LIST.
;; That means quotify each of that list's elements,
;; and preserve the other arguments in front of them.
(defun cond*-un-backquote-list* (args)
  (if (cdr args)
      (cons (car args)
            (cond*-un-backquote-list* (cdr args)))
    (mapcar (lambda (x) (list 'quote x)) (cadr (car args)))))

-- 
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] 134+ messages in thread

* Re: Code for cond*
  2024-01-25 15:37           ` JD Smith
  2024-01-25 15:44             ` Alfred M. Szmidt
@ 2024-01-29  3:19             ` Richard Stallman
  1 sibling, 0 replies; 134+ messages in thread
From: Richard Stallman @ 2024-01-29  3:19 UTC (permalink / raw)
  To: JD Smith; +Cc: monnier, enometh, 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. ]]]

  > >>   (my-cond
  > >>    (:let var value)
  > >>    (:let dvar (derived-from var))
  > >>    ((has-the-right-stuff-p dvar)
  > >>     (cons 'correct dvar))
  > >> 
  > >>    (:let foo value2)
  > >>    (:let bar (1- foo))
  > >>    ((< bar 0)
  > >>     (cons 'incorrect bar))
  > >> 
  > >>    (t nil))

On Fridat I started looking at something along the same lines.  I sent
two messages about it.  It has the same featurss as cond*, using a
syntax something like the above.

I am eager for people's responses to that idea.

-- 
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] 134+ messages in thread

* Re: Code for cond* - cond*-match, cond*-subpat and backtrack-aliases
  2024-01-29  3:19     ` Richard Stallman
@ 2024-01-29  8:54       ` Andreas Schwab
  0 siblings, 0 replies; 134+ messages in thread
From: Andreas Schwab @ 2024-01-29  8:54 UTC (permalink / raw)
  To: Richard Stallman; +Cc: Alan Mackenzie, emacs-devel

On Jan 28 2024, Richard Stallman wrote:

> "end of the text to be matched".

That's "\\'".

-- 
Andreas Schwab, SUSE Labs, schwab@suse.de
GPG Key fingerprint = 0196 BAD8 1CE9 1970 F4BE  1748 E4D4 88E3 0EEA B9D7
"And now for something completely different."



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

* Re: Code for cond* - cond*-match, cond*-subpat and backtrack-aliases
  2024-01-29  3:19     ` Richard Stallman
@ 2024-01-29 12:16       ` JD Smith
  2024-02-01  3:51         ` Richard Stallman
  0 siblings, 1 reply; 134+ messages in thread
From: JD Smith @ 2024-01-29 12:16 UTC (permalink / raw)
  To: rms; +Cc: Alan Mackenzie, emacs-devel

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

>>>>  (my-cond
>>>>   (:let var value)
>>>>   (:let dvar (derived-from var))
>>>>   ((has-the-right-stuff-p dvar)
>>>>    (cons 'correct dvar))
>>>> 
>>>>   (:let foo value2)
>>>>   (:let bar (1- foo))
>>>>   ((< bar 0)
>>>>    (cons 'incorrect bar))
>>>> 
>>>>   (t nil))
> 
> On Friday I started looking at something along the same lines.  I sent
> two messages about it.  It has the same features as cond*, using a
> syntax something like the above.

In a separate subthread (and offline) I've been discussing a simple `cond-let', envisioned as part of the if/when-let family (it has no pattern matching capabilities).  It does, as you'd expect, offer local bindings for each clause.

> On Jan 28, 2024, at 10:19 PM, Richard Stallman <rms@gnu.org> wrote:
> 
> Here is the new version.  I made a test framework for it
> and have used that to verify a number of test cases.
> ...
> 
> `(bind* BINDINGS...)' means to bind BINDINGS (as if they were in `let*')
> for the body of the clause.  As a condition, it counts as true
> if the first binding's value is non-nil.  All the bindings are made
> unconditionally for whatever scope they cover.


I wonder why (bind*) evaluates only its first value to determine if the binding condition is true?  We already have macros which evaluate the "truth of a set of bindings": the members of the if-let family.  And they 

"Evaluate each binding in turn, as in ‘let*’, stopping if a binding value is nil.  If all are non-nil return the [final] value."

I would personally find this behavior for variables bound locally within clauses easier to remember and more useful.  Users will already be familiar with it (from if-let), and it supports plain (VALUE) "bindings" for when you'd like to mix actual variable bindings and other tests in a given clause's CONDITION.

Attached is a simple cond-let design (again, just a simple extension of if/when-let, no pattern matching).  You notice that rather than using any particular keyword sigil like :let or bind*, it simply checks if the CONDITION of a clause is a list-of-lists, in which case it is interpreted as a binding spec.  The only small addition to if/when-let's binding handling is for plain (CONDITION-BINDING-SPEC) clauses; in that case, if all binding values are non-nil, the final value is returned from cond-let.



[-- Attachment #2.1: Type: text/html, Size: 6802 bytes --]

[-- Attachment #2.2: cond-let.el --]
[-- Type: application/octet-stream, Size: 2876 bytes --]

;;; cond-let.el --- cond with bindings -*- lexical-binding: t; -*-
;; Copyright (C) 2024 J.D. Smith

;;; Commentary:
;; cond-let is a macro which combines the capabilities of cond with
;; those of if-let.  The structure is the same as a cond: a sequence
;; of (condition body) forms is evaluated one by one, with the first
;; non-nil condition causing the result of its associated body to be
;; returned.  In addition to normal cond-like conditions, cond-let
;; accepts binding-spec conditions which bind variables and test their
;; values, identical to the SPEC lists in if-let.  Example:
;;
;;     (cond-let
;;       ((> x 12) (transform-it x))
;; 
;;       (((var  (other-info x))
;;         (pval (plist-get val :prop))
;;         (     (< pval 99)))
;;        (+ pval x))
;; 
;;       (t 1))
;;
;; Thanks to Stefan Monnier for valuable feedback.
;;; Code:
(require 'seq)
(eval-when-compile 'cl-lib)

(defmacro cond-let (&rest clauses)
  "Try each clause until one succeeds, possibly binding variables within them.
Each of the CLAUSES looks like (CONDITION BODY...), where
CONDITION can be a normal expression, as in `cond', or a binding
spec of the type accepted by `if-let'.  A binding spec is simply
a list of lists of the form:

   ((SYMBOL VALUEFORM) (VALUEFORM) ...)

Note that, just as in `if-let', each element of a binding spec
can take either the form (SYMBOL VALUEFORM) or just (VALUEFORM),
if only the result is of interest.  A binding spec's value is the
value of its last evaluated VALUEFORM, which means that all
values in a binding spec must be non-nil for the associated
clause to succeed.  Each SYMBOL is bound inside subsequent
VALUEFORMs within the same binding spec, as well as in the
associated clause's body (and no other clauses).

The first clause for which CONDITION evaluates to non-nil
succeeds: the expressions in its BODY are evaluated and the last
one's value becomes the value of the `cond-let' form.

If a clause has only one element, as in (CONDITION), then as a
special case `cond-let' returns CONDITION’s value, if that is
non-nil.

If no clause succeeds, `cond-let' returns nil."
  (declare (indent defun)
	   (debug (&rest [&or ((&rest [&or symbolp (symbolp form) (form)]) body)
			      (form body)])))
  (let ((cond-let (gensym "cond-let")))
    `(catch ',cond-let
       ,@(cl-loop
	  for (condition . body) in clauses
	  for clol = (and (consp condition) (consp (cdr condition))
			  (seq-every-p #'consp condition)) ; list-o-lists
	  if body collect
	  `(,(if clol 'when-let* 'when) ,condition
	    (throw ',cond-let ,(macroexp-progn body)))
	  else collect
	  (let ((clc (gensym "cond-let-cond")))
	    `(when-let* ((,clc ,(if clol `(and-let* ,condition) condition)))
	       (throw ',cond-let ,clc)))))))

(provide 'cond-let)
;;; cond-let.el ends here

[-- Attachment #2.3: Type: text/html, Size: 212 bytes --]

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

* Re: Code for cond*
  2024-01-28  3:06     ` Stefan Monnier via Emacs development discussions.
@ 2024-01-30  3:59       ` Richard Stallman
  2024-01-30 13:02         ` Stefan Monnier
  0 siblings, 1 reply; 134+ messages in thread
From: Richard Stallman @ 2024-01-30  3:59 UTC (permalink / raw)
  To: Stefan Monnier; +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. ]]]

  > > `let*' is a Lisp function, and that is reflected in the syntax for using it.
  > > Calling this `let*' would be misleading,

  > I didn't suggest to call it `let*`.
  > `pcase-let` is not called `let` and neither is `cl-macrolet` :-)

What, then, would you suggest as a name instead of bind*?
I can't see where that finger is pointing.


  >     ((bind* (x foobar) y z (foo 5) a))

  > would collide with an actual `bind*` function or macro.

The idea is that there won't be any.

  > I do like such a uniform rule.  I just think it needs to be more visible
  > than just the absence of something before the next close paren.

Would you like to show me more suggestions?  That way I could see if I like
any of them.


-- 
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] 134+ messages in thread

* Re: Code for cond*
  2024-01-30  3:59       ` Richard Stallman
@ 2024-01-30 13:02         ` Stefan Monnier
  2024-02-23  3:04           ` Richard Stallman
  0 siblings, 1 reply; 134+ messages in thread
From: Stefan Monnier @ 2024-01-30 13:02 UTC (permalink / raw)
  To: Richard Stallman; +Cc: emacs-devel

>   > > `let*' is a Lisp function, and that is reflected in the syntax for using it.
>   > > Calling this `let*' would be misleading,
>   > I didn't suggest to call it `let*`.
>   > `pcase-let` is not called `let` and neither is `cl-macrolet` :-)
> What, then, would you suggest as a name instead of bind*?
> I can't see where that finger is pointing.

`:let*`?

>   > I do like such a uniform rule.  I just think it needs to be more visible
>   > than just the absence of something before the next close paren.
> Would you like to show me more suggestions?  That way I could see if I like
> any of them.

I don't know, but other than [...] I guess you could put some keyword
before non-branches, or at the beginning of non-branches.


        Stefan




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

* Re: Code for cond*
  2024-01-28  4:16     ` Stefan Monnier via Emacs development discussions.
@ 2024-01-31  3:32       ` Richard Stallman
  2024-01-31 13:20         ` Stefan Monnier
  2024-02-13  0:41         ` Stefan Monnier
  0 siblings, 2 replies; 134+ messages in thread
From: Richard Stallman @ 2024-01-31  3:32 UTC (permalink / raw)
  To: Stefan Monnier; +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. ]]]

  > Is this what you're referring to?

  >         ((get (car subpat) 'cond*-expander)
  >          ;; Treat result as a subpattern.
  >          (cond*-subpat (funcall (get (car subpat) 'cond*-expander) subpat)
  >                        cdr-safe bindings backtrack-aliases data))

  > do we have reasons to believe that it's flexible enough?

Nobody is actually using this now, so now is the best time
to make changes -- perhaps to make it somehow more flexible.
But I have no concrete idea of what flexibility you wish for.

Can you make a concrete suggestion?  Then I might understand.

  > Pcase currently doesn't support exactly this, but does support it within
  > the backquote pattern, i.e. instead of

  >     [PAT1 PAT2 ...]

  > you have to use

  >     `[,PAT1 ,PAT2 ...]

I think that's what people will actually want to use.
I meant here to imitate pcase, but I did not remember it in detail.

So I made both forms work in cond*, but maybe the latter is the only
one we really need.

  > except that in Pcase, the value is passed as the last argument rather as
  > first argument.  I haven't seen a strong reason to prefer one over the
  > other.

With the cond* syntax for simple constrained variables,
that ordering is natural.  the variable to be bound
goes in the same spot as its value will be passed to the predicate.

 (< x 15)  as a pattern means, conceptually, bind x and evaluate (< x 15).

So the question is whether to use

  (pred < x 15)

or just

  (< x 15)

I prefer the latter because it is more concise.
The equivalent (and (pred...) car) is used often in pcase
so other forms ofit will be used often also.

-- 
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] 134+ messages in thread

* Re: Code for cond*
  2024-01-31  3:32       ` Richard Stallman
@ 2024-01-31 13:20         ` Stefan Monnier
  2024-02-03  3:32           ` Richard Stallman
  2024-02-13  0:41         ` Stefan Monnier
  1 sibling, 1 reply; 134+ messages in thread
From: Stefan Monnier @ 2024-01-31 13:20 UTC (permalink / raw)
  To: Richard Stallman; +Cc: emacs-devel

>   > Is this what you're referring to?
>
>   >         ((get (car subpat) 'cond*-expander)
>   >          ;; Treat result as a subpattern.
>   >          (cond*-subpat (funcall (get (car subpat) 'cond*-expander) subpat)
>   >                        cdr-safe bindings backtrack-aliases data))
>
>   > do we have reasons to believe that it's flexible enough?
>
> Nobody is actually using this now, so now is the best time
> to make changes -- perhaps to make it somehow more flexible.
> But I have no concrete idea of what flexibility you wish for.
>
> Can you make a concrete suggestion?  Then I might understand.

As I said: in Pcase I moved some of the "built-in" patterns, such as the
backquote, out of the core (using `pcase-defmacro` to define them
instead), as a way to make sure that `pcase-defmacro` is indeed
flexible enough.
[ And indeed, I couldn't have done that with the first version of Pcase
  because of the lack of `app`.  ]

You could do worse than trying to do exactly that for your patterns.
Tho better would be to just give up on your pattern language and (re)use
Pcase's machinery instead.  Less work, less code duplication, less
documentation duplication, less to learn for coders.  And presumably
you'd then improve Pcase, so everyone wins.


        Stefan




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

* Re: Code for cond* - cond*-match, cond*-subpat and backtrack-aliases
  2024-01-29 12:16       ` JD Smith
@ 2024-02-01  3:51         ` Richard Stallman
  2024-02-01 14:54           ` JD Smith
  0 siblings, 1 reply; 134+ messages in thread
From: Richard Stallman @ 2024-02-01  3:51 UTC (permalink / raw)
  To: JD Smith; +Cc: acm, 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. ]]]

  > I wonder why (bind*) evaluates only its first value to determine
  > if the binding condition is true?  We already have macros which
  > evaluate the "truth of a set of bindings": the members of the
  > if-let family.  And they

  > "Evaluate each binding in turn, as in ‘let*’, stopping if a
  > binding value is nil.  If all are non-nil return the [final]
  > value."

That does not seem natural to me at all.  I don't see that it is
better.

-- 
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] 134+ messages in thread

* Re: [External] : Re: Code for cond*
  2024-01-24 16:30     ` Drew Adams
@ 2024-02-01  8:56       ` Madhu
  2024-02-01 22:46         ` Emanuel Berg
  0 siblings, 1 reply; 134+ messages in thread
From: Madhu @ 2024-02-01  8:56 UTC (permalink / raw)
  To: emacs-devel

* Drew Adams <SJ0PR10MB54888A62EB6D04026A406136F37B2 @SJ0PR10MB5488.namprd10.prod.outlook.com> :
Wrote on Wed, 24 Jan 2024 16:30:01 +0000:

[JDS?]
>> 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.
>
> Indeed, this is the place for the pcase doc to _start_.
> Unfortunately, the doc doesn't do that.

"pcase" assumes that its users are already familiar with the use of
pcase from the other languages where they have used similar control flow
constructs, and it is intuitively obvious to them.  I believe this is
how it has worked, it is extremely effective in attracting programmers
of the a certain "same mindset" to come to emacs, and puts a high
barrier of entry on programmers who do not belong to that mindset,
thereby subtly shaping the programmer demographic which eventually
directs the future of emacs.


> There _is_ a pcase doc _subnode_ that covers such
> pattern-matching destructuring, but this should
> be up-front.  It's _the most important_ feature
> to point out and make clear.  It's the easiest to
> grasp and the most useful for understanding the
> rest.
>
> The subnode covering destructuring is the very
> _last_ one, "Destructuring with pcase Patterns":
>
> https://www.gnu.org/software/emacs/manual/html_node/elisp/Destructuring-with-pcase-Patterns.html
> ___
>
> I specifically suggested such doc improvements
> in an enhancement request, but they were
> unfortunately summarily dismissed just a few
> minutes after the suggestion was received:
>
> https://debbugs.gnu.org/cgi/bugreport.cgi?bug=68029#13
>
> About this I said, e.g.:
>
>   Such advantages should start with DESTRUCTURING -
>   the ONE thing you _don't_ have built-in with the
>   existing Elisp binding forms or conditional forms.
>
>   Start with a simple destructuring example:
>   `case'-like, but with destructuring.  That's my
>   advice.
>
>   Destructuring is the easiest & most powerful `pcase'
>   feature to introduce, and it's not really shown.
>   It's glossed over, at best.  Show beginners that, to
>   start with.
>
>   In a nutshell, `pase-let' vs `let' is really the
>   place to start.  _Then_ add conditional control into
>   the mix.
>
>   [The current top-level, beginning `pcase' doc is] as
>   if someone only tried to explain away some of what
>   is particularly confusing about `pcase', instead of
>   teaching the strengths of `pcase'.




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

* Re: Code for cond* - cond*-match, cond*-subpat and backtrack-aliases
  2024-02-01  3:51         ` Richard Stallman
@ 2024-02-01 14:54           ` JD Smith
  2024-02-04  4:42             ` Richard Stallman
  0 siblings, 1 reply; 134+ messages in thread
From: JD Smith @ 2024-02-01 14:54 UTC (permalink / raw)
  To: rms; +Cc: Alan Mackenzie, emacs-devel

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



> On Jan 31, 2024, at 10:51 PM, Richard Stallman <rms@gnu.org> wrote:
> 
> [[[ 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. ]]]
> 
>> I wonder why (bind*) evaluates only its first value to determine
>> if the binding condition is true?  We already have macros which
>> evaluate the "truth of a set of bindings": the members of the
>> if-let family.  And they
> 
>> "Evaluate each binding in turn, as in ‘let*’, stopping if a
>> binding value is nil.  If all are non-nil return the [final]
>> value."
> 
> That does not seem natural to me at all.  I don't see that it is
> better.

It's very natural and familiar to users of if/when/and-let (close to 1000 calls in core packages), since this description is copied verbatim from their docs.  It is a natural way to avoid pre-calculating things you don't want to calculate until a prior step holds true.  

A typical logical paths might be:

Is some variable non-nil?
Is it a plist?
Does that plist has a :foobar element?
Is that :foobar element's value a vector.
Is slot 4 of that vector non-nil?  Call it `result'. 
If all of the above are true, (do-something-with result)

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

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

* Re: [External] : Re: Code for cond*
  2024-02-01  8:56       ` Madhu
@ 2024-02-01 22:46         ` Emanuel Berg
  0 siblings, 0 replies; 134+ messages in thread
From: Emanuel Berg @ 2024-02-01 22:46 UTC (permalink / raw)
  To: emacs-devel

Madhu wrote:

> "pcase" assumes that its users are already familiar with the
> use of pcase from the other languages where they have used
> similar control flow constructs, and it is intuitively
> obvious to them. I believe this is how it has worked, it is
> extremely effective in attracting programmers of the
> a certain "same mindset" to come to emacs, and puts a high
> barrier of entry on programmers who do not belong to that
> mindset, thereby subtly shaping the programmer demographic
> which eventually directs the future of emacs.

Well, people don't come (or don't come) to Emacs because there
is some feature in the code. I don't think it works like that.

Also, don't worry about programmers coming to Emacs will have
a too high barrier. As long as they come, they will handle
it, easily.

Are people coming to Emacs like they did when I came to Emacs?
This was in the mid-late 00s. Because then it felt like
"everyone" were using it. If they don't come today as much as
they did - why is that? What features are they missing or what
pushes them away?

What are they using instead? Why? What does that have that we
don't that they like?

No ida, but I dare say `pcase' plays a very minor role in all
of that.

-- 
underground experts united
https://dataswamp.org/~incal




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

* Re: Code for cond*
  2024-01-31 13:20         ` Stefan Monnier
@ 2024-02-03  3:32           ` Richard Stallman
  2024-02-03  6:09             ` Stefan Monnier
  0 siblings, 1 reply; 134+ messages in thread
From: Richard Stallman @ 2024-02-03  3:32 UTC (permalink / raw)
  To: Stefan Monnier; +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. ]]]

  > As I said: in Pcase I moved some of the "built-in" patterns, such as the
  > backquote, out of the core (using `pcase-defmacro` to define them
  > instead), as a way to make sure that `pcase-defmacro` is indeed
  > flexible enough.

You're suggesting that I do this by exploring, but that's the hard
way.  Harder than actually necessary -- since the real intention of a
macro facility is for defining other kinds of pattern _given the ones
that will be provided_.  It's comparable to looking for axioms for
mathematics (or even just plane geometry) -- interesting, but there
was no need for the rest of mathematics to wait for this to work.

I would rather install it the way it is.

  > Tho better would be to just give up on your pattern language and (re)use
  > Pcase's machinery instead.

I wouldn't want to use it without making it include the improvements
I've designed into cond*, and I expect that `pcase-defmacro' is not
capable of implementing constrained variables or `cdr-ignore'.  That
would require changing the pcase pattern code.

I've explained why it is not feasible for me to work on those changes,
and why I expect that code won't work in cond* at all without
substantial additional changes.

I have no feasible choice except to use the code I have written.

If you tell me the name of the function in pcase which is the entry
point for processing a pattern, with that starting point I might be
able to understand some of that code.  I doubt it will change the
situation, but I will take a look.



-- 
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] 134+ messages in thread

* Re: Code for cond*
  2024-02-03  3:32           ` Richard Stallman
@ 2024-02-03  6:09             ` Stefan Monnier
  2024-02-03  6:48               ` Emanuel Berg
                                 ` (2 more replies)
  0 siblings, 3 replies; 134+ messages in thread
From: Stefan Monnier @ 2024-02-03  6:09 UTC (permalink / raw)
  To: Richard Stallman; +Cc: emacs-devel

>   > As I said: in Pcase I moved some of the "built-in" patterns, such as the
>   > backquote, out of the core (using `pcase-defmacro` to define them
>   > instead), as a way to make sure that `pcase-defmacro` is indeed
>   > flexible enough.
>
> You're suggesting that I do this by exploring, but that's the hard
> way.  Harder than actually necessary -- since the real intention of a
> macro facility is for defining other kinds of pattern _given the ones
> that will be provided_.  It's comparable to looking for axioms for
> mathematics (or even just plane geometry) -- interesting, but there
> was no need for the rest of mathematics to wait for this to work.

What have it tried to implement using the macro facility you designed
for your patterns?  Have you tried to implement, say the `map` pattern
we have in `map.el` or the `cl-struct` pattern?

> I wouldn't want to use it without making it include the improvements
> I've designed into cond*, and I expect that `pcase-defmacro' is not
> capable of implementing constrained variables or `cdr-ignore'.

Your expectation is incorrect.

> That would require changing the pcase pattern code.

I don't think so.  The primitive patterns were carefully chosen so it
shouldn't be needed.  E.g.:

    (pcase-defmacro constrain (symbol exp) `(and ,symbol (guard ,exp)))

Admittedly, for `cdr-ignore` it would take more than a one-liner,
because it affects the interpretation of all the patterns within it
(which makes its meaning somewhat unclear: should `(constrain x (null
(cdr x)))` be ignored if it appears within a `cdr-ignore`?).
It might be the case that a clean implementation would require some
adjustments to the implementation of other patterns.  The details
would matter.

> If you tell me the name of the function in pcase which is the entry
> point for processing a pattern, with that starting point I might be
> able to understand some of that code.  I doubt it will change the
> situation, but I will take a look.

As I said, you don't need to know the internals, as shown in the
PoC below.  Which adds a `pcase*` construct to your `cond*`. It's used
like your `match*` but with Pcase patterns.
[ The patch also fixes some misuses of ";;; " which are reserved for
  sectioning and mess up `outline-minor-mode`.  ]

Also, BTW, I don't understand this behavior of your code:

    (macroexpand '(cond* ((match* `(c ,@d ,e) DAT) BRANCH1)))
==>
    (let ((d2624 DAT))
     (if (and (consp d2624) (eq 'c (car d2624)) (append (cdr d2624) (list e)))
         (let* ((d (cdr d2624))) BRANCH1)
       nil))


        Stefan


diff --git a/lisp/cond*.el b/lisp/cond*.el
index e40543ce393..d25bd858324 100644
--- a/lisp/cond*.el
+++ b/lisp/cond*.el
@@ -218,8 +218,8 @@ cond*-convert-condition
                    ;; Then always go on to run the UNCONDIT-CLAUSES.
                    (if true-exps
                        `(let ((,init-gensym ,first-value))
-;;; ??? Should we make the bindings a second time for the UNCONDIT-CLAUSES.
-;;; as the doc string says, for uniformity with match*?
+;;;  ??? Should we make the bindings a second time for the UNCONDIT-CLAUSES.
+;;;  as the doc string says, for uniformity with match*?
                           (let* ,mod-bindings
                             (when ,init-gensym
                               . ,true-exps)
@@ -235,6 +235,18 @@ cond*-convert-condition
                     (let* ,mod-bindings
                       (when ,init-gensym
                         . ,true-exps)))))))
+          ((eq pat-type 'pcase*)
+           (if true-exps
+               (progn
+                 (cl-assert (null uncondit-clauses))
+                 (cl-assert (or (null iffalse) rest))
+                 `(pcase ,(nth 2 condition)
+                    (,(nth 1 condition) ,@true-exps)
+                    (_ ,iffalse)))
+             (cl-assert (null iffalse))
+             (cl-assert (null rest))
+             `(pcase-let ((,(nth 1 condition) ,(nth 2 condition)))
+                (cond* . ,uncondit-clauses))))
           ((eq pat-type 'match*)
            (cond*-match condition true-exps uncondit-clauses iffalse))
           (t
@@ -340,8 +352,8 @@ cond*-bind-pattern-syms
 
 (defvar cond*-debug-pattern nil)
 
-;;; ??? Structure type patterns not implemented yet.
-;;; ??? Probably should optimize the `nth' calls in handling `list'.
+;;;  ??? Structure type patterns not implemented yet.
+;;;  ??? Probably should optimize the `nth' calls in handling `list'.
 
 (defun cond*-subpat (subpat cdr-ignore bindings inside-or backtrack-aliases data)
   "Generate code to match ibe subpattern within `match*'.




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

* Re: Code for cond*
  2024-02-03  6:09             ` Stefan Monnier
@ 2024-02-03  6:48               ` Emanuel Berg
  2024-02-04  4:46               ` Richard Stallman
  2024-02-04  4:46               ` Richard Stallman
  2 siblings, 0 replies; 134+ messages in thread
From: Emanuel Berg @ 2024-02-03  6:48 UTC (permalink / raw)
  To: emacs-devel

Stefan Monnier wrote:

> What have it tried to implement using the macro facility you
> designed for your patterns? Have you tried to implement, say
> the `map` pattern we have in `map.el` or the
> `cl-struct` pattern?

`map', a `pcase-defmacro'. Or `cl-struct'.

To implement cond*, an interface introduced because `pcase'
and `cl-lib' were two interfaces too many; and, as an
alternative to pcase because pcase has made Elisp too
difficult to maintain - which is also what cl-lib has done
to Elisp.

A clever move!

But can cond* regain the initiative?

-- 
underground experts united
https://dataswamp.org/~incal




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

* Re: Code for cond* - cond*-match, cond*-subpat and backtrack-aliases
  2024-02-01 14:54           ` JD Smith
@ 2024-02-04  4:42             ` Richard Stallman
  0 siblings, 0 replies; 134+ messages in thread
From: Richard Stallman @ 2024-02-04  4:42 UTC (permalink / raw)
  To: JD Smith; +Cc: acm, 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. ]]]

  > A typical logical paths might be:

  > Is some variable non-nil?
  > Is it a plist?
  > Does that plist has a :foobar element?
  > Is that :foobar element's value a vector.
  > Is slot 4 of that vector non-nil?  Call it `result'. 
  > If all of the above are true, (do-something-with result)

Maybe that should be another form of cond* clause.
I will think about it.  Any concrete ideas?

-- 
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] 134+ messages in thread

* Re: Code for cond*
  2024-02-03  6:09             ` Stefan Monnier
  2024-02-03  6:48               ` Emanuel Berg
@ 2024-02-04  4:46               ` Richard Stallman
  2024-02-04 14:04                 ` Stefan Monnier
  2024-02-04  4:46               ` Richard Stallman
  2 siblings, 1 reply; 134+ messages in thread
From: Richard Stallman @ 2024-02-04  4:46 UTC (permalink / raw)
  To: Stefan Monnier; +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. ]]]

  > What have it tried to implement using the macro facility you designed
  > for your patterns?  Have you tried to implement, say the `map` pattern
  > we have in `map.el` or the `cl-struct` pattern?

I don't know if it can be used to implement something that is
different in hte low level.  But I could try it, if I knew what those
patterns precisely do.  For structures, I'd nee dto know the details
of how structure types get recorded.

-- 
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] 134+ messages in thread

* Re: Code for cond*
  2024-02-03  6:09             ` Stefan Monnier
  2024-02-03  6:48               ` Emanuel Berg
  2024-02-04  4:46               ` Richard Stallman
@ 2024-02-04  4:46               ` Richard Stallman
  2024-02-04 13:58                 ` Stefan Monnier
  2024-02-13  0:48                 ` Stefan Monnier
  2 siblings, 2 replies; 134+ messages in thread
From: Richard Stallman @ 2024-02-04  4:46 UTC (permalink / raw)
  To: Stefan Monnier; +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. ]]]

  > I don't think so.  The primitive patterns were carefully chosen so it
  > shouldn't be needed.  E.g.:

  >     (pcase-defmacro constrain (symbol exp) `(and ,symbol (guard ,exp)))

You're talking about one of the two kinds of constrained variables --
the more compex and general kind.  The simple kind is (PREDICATE VAR
OTHER-ARGS).  I am pretty sure a simple macro facility can't handle
tha because it isn't identified by pne specific symbol at the start,

  >  should `(constrain x (null
  > (cdr x)))` be ignored if it appears within a `cdr-ignore`?).

It affects only the built-in patterns that destructure cons cells
and lists


-- 
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] 134+ messages in thread

* Re: Code for cond*
  2024-02-04  4:46               ` Richard Stallman
@ 2024-02-04 13:58                 ` Stefan Monnier
  2024-02-13  0:48                 ` Stefan Monnier
  1 sibling, 0 replies; 134+ messages in thread
From: Stefan Monnier @ 2024-02-04 13:58 UTC (permalink / raw)
  To: Richard Stallman; +Cc: emacs-devel

>   > I don't think so.  The primitive patterns were carefully chosen so it
>   > shouldn't be needed.  E.g.:
>
>   >     (pcase-defmacro constrain (symbol exp) `(and ,symbol (guard ,exp)))
>
> You're talking about one of the two kinds of constrained variables --
> the more compex and general kind.  The simple kind is (PREDICATE VAR
> OTHER-ARGS).  I am pretty sure a simple macro facility can't handle
> tha because it isn't identified by pne specific symbol at the start,

That's true.  It would be a simple change.  But we could
provide the same facility with a different syntax which does come with
a specific symbol at the start.


        Stefan




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

* Re: Code for cond*
  2024-02-04  4:46               ` Richard Stallman
@ 2024-02-04 14:04                 ` Stefan Monnier
  0 siblings, 0 replies; 134+ messages in thread
From: Stefan Monnier @ 2024-02-04 14:04 UTC (permalink / raw)
  To: Richard Stallman; +Cc: emacs-devel

>   > What have it tried to implement using the macro facility you designed
>   > for your patterns?  Have you tried to implement, say the `map` pattern
>   > we have in `map.el` or the `cl-struct` pattern?
>
> I don't know if it can be used to implement something that is
> different in hte low level.  But I could try it, if I knew what those
> patterns precisely do.  For structures, I'd nee dto know the details
> of how structure types get recorded.

You shouldn't need to know anything more than what's already present in
the definition of those patterns.

    grep pcase-defmacro.\*cl-struct **/*.el
and
    grep pcase-defmacro.\*map **/*.el

should let you find those definitions easily enough.


        Stefan




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

* Re: Code for cond*
  2024-01-31  3:32       ` Richard Stallman
  2024-01-31 13:20         ` Stefan Monnier
@ 2024-02-13  0:41         ` Stefan Monnier
  2024-02-23  3:04           ` Richard Stallman
  1 sibling, 1 reply; 134+ messages in thread
From: Stefan Monnier @ 2024-02-13  0:41 UTC (permalink / raw)
  To: Richard Stallman; +Cc: emacs-devel

> With the cond* syntax for simple constrained variables,
> that ordering is natural.  the variable to be bound
> goes in the same spot as its value will be passed to the predicate.
>
>  (< x 15)  as a pattern means, conceptually, bind x and evaluate (< x 15).
>
> So the question is whether to use
>
>   (pred < x 15)
>
> or just
>
>   (< x 15)
>
> I prefer the latter because it is more concise.
> The equivalent (and (pred...) car) is used often in pcase
> so other forms ofit will be used often also.

As mentioned in an email I had sent before, I agree that it'd be nice
for `pred` to be able to bind a variable, since indeed when that's
needed the `(and (pred ...) VAR)` form is rather inconveniently verbose.

Your syntax (FUN VAR ARGS...) has two disadvantages in my view:

- It fixes the argument to be checked as the first argument.
- It does not syntactically distinguish the VAR from the normal
  arguments, making a bit too magical for my taste.

Maybe a good alternative is to annotate the special arg with a comma:

    (pred (FUN ,VAR ARGS...))

this way we can trivially support other placements:

    (pred (FUN ARG1 ARG2 ,VAR ARGS...))

Of course, there remains the question whether this usage should be the
one that gets the privilege of not needing a dedicated "keyword", i.e. to
allowing using it without the surrounding `pred`:

    (FUN ,VAR ARGS...)

Until now, in `pcase` I refrained from eating this huge chunk of
the namespace.


        Stefan




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

* Re: Code for cond*
  2024-02-04  4:46               ` Richard Stallman
  2024-02-04 13:58                 ` Stefan Monnier
@ 2024-02-13  0:48                 ` Stefan Monnier
  2024-02-13  2:27                   ` Stefan Monnier
  2024-02-14 11:16                   ` Richard Stallman
  1 sibling, 2 replies; 134+ messages in thread
From: Stefan Monnier @ 2024-02-13  0:48 UTC (permalink / raw)
  To: Richard Stallman; +Cc: emacs-devel

> > Admittedly, for `cdr-ignore` it would take more than a one-liner,
> > because it affects the interpretation of all the patterns within it
> > (which makes its meaning somewhat unclear: should `(constrain x (null
> > (cdr x)))` be ignored if it appears within a `cdr-ignore`?).
> It affects only the built-in patterns that destructure cons cells
> and lists

That still doesn't clarify its meaning.
E.g. I understand that `(head ,b) should match (head 1 2), but what
about matching a value like (head) ?
The code I have found so far makes it not match (head).
How should this be documented?
Why was this chosen?

The main use-case I can think of for `cdr-ignore` is when matching
against things like Lisp code, where a pattern like

    `(lambda ,args . ,body)

should also match (lambda) because (lambda) is a (broken) lambda
expression rather than being something *else* than a lambda expression.

So I'm not sure the behavior you chose for `cdr-ignore` is the one
we want.


        Stefan




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

* Re: Code for cond*
  2024-02-13  0:48                 ` Stefan Monnier
@ 2024-02-13  2:27                   ` Stefan Monnier
  2024-02-14 11:16                   ` Richard Stallman
  1 sibling, 0 replies; 134+ messages in thread
From: Stefan Monnier @ 2024-02-13  2:27 UTC (permalink / raw)
  To: Richard Stallman; +Cc: emacs-devel

> The main use-case I can think of for `cdr-ignore` is when matching
> against things like Lisp code, where a pattern like
>
>     `(lambda ,args . ,body)
>
> should also match (lambda) because (lambda) is a (broken) lambda
> expression rather than being something *else* than a lambda expression.
>
> So I'm not sure the behavior you chose for `cdr-ignore` is the one
> we want.

BTW, here's what I used at one place to address this problem:

    (defmacro byte-optimize--pcase (exp &rest cases)
      ;; When we do
      ;;
      ;;     (pcase EXP
      ;;       (`(if ,exp ,then ,else) (DO-TEST))
      ;;       (`(plus ,e2 ,e2)        (DO-ADD))
      ;;       (`(times ,e2 ,e2)       (DO-MULT))
      ;;       ...)
      ;;
      ;; we usually don't want to fall back to the default case if
      ;; the value of EXP is of a form like `(if E1 E2)' or `(plus E1)'
      ;; or `(times E1 E2 E3)', instead we either want to signal an error
      ;; that EXP has an unexpected shape, or we want to carry on as if
      ;; it had the right shape (ignore the extra data and pretend the missing
      ;; data is nil) because it should simply never happen.
      ;;
      ;; The macro below implements the second option by rewriting patterns
      ;; like `(if ,exp ,then ,else)'
      ;; to   `(if . (or `(,exp ,then ,else) pcase--dontcare))'.
      ;;
      ;; The resulting macroexpansion is also significantly cleaner/smaller/faster.
      (declare (indent 1) (debug pcase))
      `(pcase ,exp
         . ,(mapcar (lambda (case)
                      `(,(pcase (car case)
                           ((and `(,'\` (,_ . (,'\, ,_))) pat) pat)
                           (`(,'\` (,head . ,tail))
                            (list '\`
                                  (cons head
                                        (list '\, `(or ,(list '\` tail) pcase--dontcare)))))
                           (pat pat))
                        . ,(cdr case)))
                    cases)))

- Stefan




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

* Re: Code for cond*
  2024-02-13  0:48                 ` Stefan Monnier
  2024-02-13  2:27                   ` Stefan Monnier
@ 2024-02-14 11:16                   ` Richard Stallman
  2024-02-14 12:45                     ` Stefan Monnier
  1 sibling, 1 reply; 134+ messages in thread
From: Richard Stallman @ 2024-02-14 11:16 UTC (permalink / raw)
  To: Stefan Monnier; +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. ]]]

  > That still doesn't clarify its meaning.
  > E.g. I understand that `(head ,b) should match (head 1 2), but what
  > about matching a value like (head) ?
  > The code I have found so far makes it not match (head).

That is right.  `(head ,b) insists there must be two elements in the list,
and binds b to the second element.  The ignore-cdrs flag has no effect on that.
This flag affects only cdrs which are nil in the pattern.

What it affects is whether `(head ,b) can match longer lists such as
(head foo bar) -- whether the pattern demands that the cddr be nil to
match.  I had this idea when I saw ` . ,_' at the end of lists in
several pcase patterns.

Its current meaning is the right meaning.

I believe that it IS documented in these lines.

    (cdr PATTERN)  matches PATTERN with strict checking of cdrs.
      That means that `list' patterns verify that the final cdr is nil.
      Strict checking is the default.
    (cdr-safe PATTERN)  matches PATTERN with lax checking of cdrs.
      That means that `list' patterns do not examine the final cdr.

-- 
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] 134+ messages in thread

* Re: Code for cond*
  2024-02-14 11:16                   ` Richard Stallman
@ 2024-02-14 12:45                     ` Stefan Monnier
  2024-02-22  3:05                       ` Richard Stallman
  0 siblings, 1 reply; 134+ messages in thread
From: Stefan Monnier @ 2024-02-14 12:45 UTC (permalink / raw)
  To: Richard Stallman; +Cc: emacs-devel

> Its current meaning is the right meaning.

I see fundamentally two decisions in the design:

- Whether the "lax"ness applies only to the final cdr.
- Whether the "lax"ness applies also to the (final) cdrs of nested lists.

Any evidence to substantiate this wild claim that your choices (which
answer yes to both questions, AFAICT) are The Right Ones?

IMO, there is no right or wrong answer here, only answers
which are more often or less often useful.  I gave an argument why
"yes" may not be the best answer.  Could you at least give me some hint
of cases you've seen where your choices are the ones we need?

> I believe that it IS documented in these lines.
>
>     (cdr PATTERN)  matches PATTERN with strict checking of cdrs.
>       That means that `list' patterns verify that the final cdr is nil.
>       Strict checking is the default.
>     (cdr-safe PATTERN)  matches PATTERN with lax checking of cdrs.
>       That means that `list' patterns do not examine the final cdr.

I guess the "final cdr is nil" does, indeed, thanks.
So it's not "all cdrs" it's "the final cdrs".
It would be good to find a name that conveys this more clearly.
Maybe `lax-list-ends`?


        Stefan




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

* Re: Code for cond*
  2024-02-14 12:45                     ` Stefan Monnier
@ 2024-02-22  3:05                       ` Richard Stallman
  2024-02-22  4:08                         ` Stefan Monnier
  0 siblings, 1 reply; 134+ messages in thread
From: Richard Stallman @ 2024-02-22  3:05 UTC (permalink / raw)
  To: Stefan Monnier; +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. ]]]

  > - Whether the "lax"ness applies only to the final cdr.
  > - Whether the "lax"ness applies also to the (final) cdrs of nested lists.

  > Any evidence to substantiate this wild claim that your choices (which
  > answer yes to both questions, AFAICT) are The Right Ones?

If you raise the question without insulting anyone or and without
presuming I'm wrong, I will respond without insulting anyone and
without presuming I'm right.


-- 
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] 134+ messages in thread

* Re: Code for cond*
  2024-02-22  3:05                       ` Richard Stallman
@ 2024-02-22  4:08                         ` Stefan Monnier
  2024-02-25  3:14                           ` Richard Stallman
  0 siblings, 1 reply; 134+ messages in thread
From: Stefan Monnier @ 2024-02-22  4:08 UTC (permalink / raw)
  To: Richard Stallman; +Cc: emacs-devel

>   > - Whether the "lax"ness applies only to the final cdr.
>   > - Whether the "lax"ness applies also to the (final) cdrs of nested lists.
>
>   > Any evidence to substantiate this wild claim that your choices (which
>   > answer yes to both questions, AFAICT) are The Right Ones?
>
> If you raise the question without insulting anyone or and without
> presuming I'm wrong, I will respond without insulting anyone and
> without presuming I'm right.

I think claiming that you have "the right meaning" is weird, because
I think there's no right or wrong, there are just a variety of options
whose adequacy depends on the circumstances, so some are more often
useful and others less so.

I can't think of any part of Pcase's pattern syntax which I could
describe as providing "the right meaning": they're all adjusted as best
as I could to the needs I saw, but it's all just the result of a bunch
of tradeoffs.

That's what I consider "wild" about your claim.


        Stefan




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

* Re: Code for cond*
  2024-01-30 13:02         ` Stefan Monnier
@ 2024-02-23  3:04           ` Richard Stallman
  0 siblings, 0 replies; 134+ messages in thread
From: Richard Stallman @ 2024-02-23  3:04 UTC (permalink / raw)
  To: Stefan Monnier; +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. ]]]

  > `:let*`?

If everyone prefers this, I'll go along with that preference.  But I
think that name would be misleading.  :bind is roughly similar to let*
but they are different enough that use of the name let* for the former
could cause confusion.

  > I don't know, but other than [...] I guess you could put some keyword
  > before non-branches, or at the beginning of non-branches.

The current version of cond* supports both.  This is described
in the doc string.
-- 
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] 134+ messages in thread

* Re: Code for cond*
  2024-02-13  0:41         ` Stefan Monnier
@ 2024-02-23  3:04           ` Richard Stallman
  2024-02-23 13:39             ` Stefan Monnier
  0 siblings, 1 reply; 134+ messages in thread
From: Richard Stallman @ 2024-02-23  3:04 UTC (permalink / raw)
  To: Stefan Monnier; +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. ]]]

  > Your syntax (FUN VAR ARGS...) has two disadvantages in my view:

  > - It fixes the argument to be checked as the first argument.
  > - It does not syntactically distinguish the VAR from the normal
  >   arguments, making a bit too magical for my taste.

I think this is not a real disadvantage.  The more general kind of
constrained variable, with `constrain', does not have those two
limitations.

The simple version variable, (PRED VAR OTHER-ARGS...), is an
abbreviation which handles the usual cases in a very convenient way.
For the cases it does not handle, just use `constrain'.

The special nature of the first argument is something that users
will get used to as they see many instances of cond*.

  > Of course, there remains the question whether this usage should be the
  > one that gets the privilege of not needing a dedicated "keyword", i.e. to
  > allowing using it without the surrounding `pred`:

  >     (FUN ,VAR ARGS...)

If this syntax is easy to implement and doesn't cause difficulties, I'll
go along with it if people prefer it.



-- 
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] 134+ messages in thread

* Re: Code for cond*
  2024-02-23  3:04           ` Richard Stallman
@ 2024-02-23 13:39             ` Stefan Monnier
  2024-02-25  3:16               ` Richard Stallman
  0 siblings, 1 reply; 134+ messages in thread
From: Stefan Monnier @ 2024-02-23 13:39 UTC (permalink / raw)
  To: Richard Stallman; +Cc: emacs-devel

>   > Your syntax (FUN VAR ARGS...) has two disadvantages in my view:
>   > - It fixes the argument to be checked as the first argument.
>   > - It does not syntactically distinguish the VAR from the normal
>   >   arguments, making a bit too magical for my taste.
> I think this is not a real disadvantage.

What makes you think so?
I doubt you'd consider coders' confusion as a feature, so what makes you
think people won't find it confusing (and/or get confused, even when
they may not realize it's confusing)?

> The more general kind of constrained variable, with `constrain', does
> not have those two limitations.

Agreed.  `constrain` is fine by me (I'd prefer a shorter word, but for
the sake of avoiding bikeshedding, I'm fine with it).

> The special nature of the first argument is something that users
> will get used to as they see many instances of cond*.

That could turn into "it's a pain they'll learn to live with".
The question is whether introducing the risk of such problems is worth
the expected upside.

I suggest you grep for `(and.*(pred` to see which existing uses of
Pcase's `pred` could take advantage of that shorthand, so we have
a better idea of what's the expected upside.

>   > Of course, there remains the question whether this usage should be
>   > the one that gets the privilege of not needing a dedicated
>   > "keyword", i.e. to allowing using it without the surrounding
>   > `pred`:
>   >     (FUN ,VAR ARGS...)
>
> If this syntax is easy to implement and doesn't cause difficulties, I'll
> go along with it if people prefer it.

That doesn't answer the important part of my question:

    Of course, there remains the question whether this usage should be
    the one that gets the privilege of not needing a dedicated
    "keyword", i.e. to allowing using it without the surrounding `pred`.


-- Stefan




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

* Re: Code for cond*
  2024-02-22  4:08                         ` Stefan Monnier
@ 2024-02-25  3:14                           ` Richard Stallman
  2024-02-25 15:03                             ` Stefan Monnier
  0 siblings, 1 reply; 134+ messages in thread
From: Richard Stallman @ 2024-02-25  3:14 UTC (permalink / raw)
  To: Stefan Monnier; +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. ]]]

  > I think there's no right or wrong, there are just a variety of options
  > whose adequacy depends on the circumstances, so some are more often
  > useful and others less so.

That is what I think too about this point.

  > I think claiming that you have "the right meaning" is weird, because

By "the right meaning" I mean the right choice, judged on the
practical criteria.

If what you want is to ignore the last cdr of one specific list, it is
hard to find anything simpler than `. ,_'.  But that approach becomes
messy when you need to put it at the end of many lists in one pattern.
One of the first pcase examples I saw needed to do that.

So I added a way to change the default locally for whether to check
the last cdr or not.

-- 
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] 134+ messages in thread

* Re: Code for cond*
  2024-02-23 13:39             ` Stefan Monnier
@ 2024-02-25  3:16               ` Richard Stallman
  2024-02-25 14:57                 ` Alfred M. Szmidt
  2024-02-25 17:10                 ` Stefan Monnier
  0 siblings, 2 replies; 134+ messages in thread
From: Richard Stallman @ 2024-02-25  3:16 UTC (permalink / raw)
  To: Stefan Monnier; +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. ]]]

  > >   > Your syntax (FUN VAR ARGS...) has two disadvantages in my view:
  > >   > - It fixes the argument to be checked as the first argument.
  > >   > - It does not syntactically distinguish the VAR from the normal
  > >   >   arguments, making a bit too magical for my taste.
  > > I think this is not a real disadvantage.

  > What makes you think so?
  > I doubt you'd consider coders' confusion as a feature, so what makes you
  > think people won't find it confusing (and/or get confused, even when
  > they may not realize it's confusing)?

It is less unnatural than the pcase patterns for the same things,
such as `pred', so I think any confusion from it will be mild.

      > Of course, there remains the question whether this usage should be
      > the one that gets the privilege of not needing a dedicated
      > "keyword", i.e. to allowing using it without the surrounding `pred`.

I expect this to be frequent, so people will get used to it.
Thus. the cost of learning it will be distributed across many uses,
and users will appreciate the brevity often.

-- 
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] 134+ messages in thread

* Re: Code for cond*
  2024-02-25  3:16               ` Richard Stallman
@ 2024-02-25 14:57                 ` Alfred M. Szmidt
  2024-02-25 15:38                   ` Stefan Monnier
  2024-02-25 17:10                 ` Stefan Monnier
  1 sibling, 1 reply; 134+ messages in thread
From: Alfred M. Szmidt @ 2024-02-25 14:57 UTC (permalink / raw)
  To: rms; +Cc: monnier, emacs-devel

On a slightly different topic, when will cond* be added/commited to
GNU Emacs?  Seeing that how cond* works, and documentation has
slightly materialized, it would be good to get some milage.



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

* Re: Code for cond*
  2024-02-25  3:14                           ` Richard Stallman
@ 2024-02-25 15:03                             ` Stefan Monnier
  2024-02-29  3:50                               ` Richard Stallman
  2024-02-29  3:50                               ` Richard Stallman
  0 siblings, 2 replies; 134+ messages in thread
From: Stefan Monnier @ 2024-02-25 15:03 UTC (permalink / raw)
  To: Richard Stallman; +Cc: emacs-devel

> If what you want is to ignore the last cdr of one specific list, it is
> hard to find anything simpler than `. ,_'.  But that approach becomes
> messy when you need to put it at the end of many lists in one pattern.
> One of the first pcase examples I saw needed to do that.
[...]
> So I added a way to change the default locally for whether to check
> the last cdr or not.

Hmm... that leaves more questions open:

- Which example was that?  Have you found many others?
- What do you reply to my own experience that we often want to accept
  not only longer lists but also shorter lists?
- It doesn't explain why you apply it recursively.
- As you say for "one specific list, it is hard to find anything simpler
  than `. ,_'", yet you added `cdr-safe` which applies to a single
  pattern, and thus typically to a single list, unless the pattern is
  a big (or ...) pattern which are rather unusual.

BTW, regarding "anything simpler than `. ,_'", there is ",@_".
Whether it's "simpler" is debatable, of course, but it is one
char shorter.  Pcase doesn't support it, but I've hesitated to add
support for it (the problem is that general support for ,@ in backquote
patterns is somewhere between hard and undesirable, so I'm not sure it's
worth adding support for it only in those cases where it can be
supported satisfactorily).


        Stefan




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

* Re: Code for cond*
  2024-02-25 14:57                 ` Alfred M. Szmidt
@ 2024-02-25 15:38                   ` Stefan Monnier
  2024-02-25 16:42                     ` Alfred M. Szmidt
  2024-02-25 17:22                     ` Alan Mackenzie
  0 siblings, 2 replies; 134+ messages in thread
From: Stefan Monnier @ 2024-02-25 15:38 UTC (permalink / raw)
  To: Alfred M. Szmidt; +Cc: rms, emacs-devel

> On a slightly different topic, when will cond* be added/commited to
> GNU Emacs?  Seeing that how cond* works, and documentation has
> slightly materialized, it would be good to get some milage.

FWIW, I'm opposed to adding it without first unifying the Pcase and
`match*` pattern languages (including making sure that third party
packages which define their own patterns don't need to define them
separately for `pcase` and for `match*`).
[ It's trivial to do by making `match*` reuse the Pcase machinery, so
  there is no serious obstacle for that.  ]


        Stefan




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

* Re: Code for cond*
  2024-02-25 15:38                   ` Stefan Monnier
@ 2024-02-25 16:42                     ` Alfred M. Szmidt
  2024-02-25 17:13                       ` Stefan Monnier
  2024-02-25 17:22                     ` Alan Mackenzie
  1 sibling, 1 reply; 134+ messages in thread
From: Alfred M. Szmidt @ 2024-02-25 16:42 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: rms, emacs-devel


   > On a slightly different topic, when will cond* be added/commited to
   > GNU Emacs?  Seeing that how cond* works, and documentation has
   > slightly materialized, it would be good to get some milage.

   FWIW, I'm opposed to adding it without first unifying the Pcase and
   `match*` pattern languages (including making sure that third party
   packages which define their own patterns don't need to define them
   separately for `pcase` and for `match*`).
   [ It's trivial to do by making `match*` reuse the Pcase machinery, so
     there is no serious obstacle for that.  ]

Would you like to do so?   




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

* Re: Code for cond*
  2024-02-25  3:16               ` Richard Stallman
  2024-02-25 14:57                 ` Alfred M. Szmidt
@ 2024-02-25 17:10                 ` Stefan Monnier
  2024-02-27  3:11                   ` Richard Stallman
  1 sibling, 1 reply; 134+ messages in thread
From: Stefan Monnier @ 2024-02-25 17:10 UTC (permalink / raw)
  To: Richard Stallman; +Cc: emacs-devel

>   > What makes you think so?
>   > I doubt you'd consider coders' confusion as a feature, so what makes you
>   > think people won't find it confusing (and/or get confused, even when
>   > they may not realize it's confusing)?
> It is less unnatural than the pcase patterns for the same things,
> such as `pred', so I think any confusion from it will be mild.

I for one find your syntax a lot more unnatural than the (pred
SYMBOL) syntax, so what is natural is in the eye of the beholder
(despite your use of "it is" which makes it sound like it's a plain
fact rather than an opinion).

The fact that a pattern like (< x y) will treat `y` as a reference to an
existing variable but `x` as the binding site for a new` x` variable is
what I find too risky.

That's why I suggest the use of "," as in (< ,x y) which makes the
difference more clear (and leans on the use of , in the backquote
patterns where it similarly indicates places where a new variable is
bound).

>       > Of course, there remains the question whether this usage should be
>       > the one that gets the privilege of not needing a dedicated
>       > "keyword", i.e. to allowing using it without the surrounding `pred`.
>
> I expect this to be frequent, so people will get used to it.
> Thus. the cost of learning it will be distributed across many uses,
> and users will appreciate the brevity often.

That's also what I thought about the `pcase` patterns.
Yet, 14 years alter, we still have regular threads about how its syntax
is impenetrable, so much so that you went through the trouble of
implementing a "replacement" (which ends up inventing the same wheel).
:-)


        Stefan




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

* Re: Code for cond*
  2024-02-25 16:42                     ` Alfred M. Szmidt
@ 2024-02-25 17:13                       ` Stefan Monnier
  0 siblings, 0 replies; 134+ messages in thread
From: Stefan Monnier @ 2024-02-25 17:13 UTC (permalink / raw)
  To: Alfred M. Szmidt; +Cc: rms, emacs-devel

>    > On a slightly different topic, when will cond* be added/commited to
>    > GNU Emacs?  Seeing that how cond* works, and documentation has
>    > slightly materialized, it would be good to get some milage.
>
>    FWIW, I'm opposed to adding it without first unifying the Pcase and
>    `match*` pattern languages (including making sure that third party
>    packages which define their own patterns don't need to define them
>    separately for `pcase` and for `match*`).
>    [ It's trivial to do by making `match*` reuse the Pcase machinery, so
>      there is no serious obstacle for that.  ]
>
> Would you like to do so?   

I already sent a patch which does that.


        Stefan




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

* Re: Code for cond*
  2024-02-25 15:38                   ` Stefan Monnier
  2024-02-25 16:42                     ` Alfred M. Szmidt
@ 2024-02-25 17:22                     ` Alan Mackenzie
  2024-02-25 17:46                       ` Alfred M. Szmidt
  1 sibling, 1 reply; 134+ messages in thread
From: Alan Mackenzie @ 2024-02-25 17:22 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Alfred M. Szmidt, rms, emacs-devel

Hello, Stefan.

On Sun, Feb 25, 2024 at 10:38:46 -0500, Stefan Monnier wrote:
> > On a slightly different topic, when will cond* be added/commited to
> > GNU Emacs?  Seeing that how cond* works, and documentation has
> > slightly materialized, it would be good to get some milage.

I agree.

> FWIW, I'm opposed to adding it without first unifying the Pcase and
> `match*` pattern languages (including making sure that third party
> packages which define their own patterns don't need to define them
> separately for `pcase` and for `match*`).

> [ It's trivial to do by making `match*` reuse the Pcase machinery, so
>   there is no serious obstacle for that.  ]

I hope that cond* won't be written on top of pcase.  The pcase machinery
is almost entirely undocumented (and even the "almost" might be an
exaggeration), and that doesn't look like changing any time soon.

Debugging pcase itself, or a backtrace involving one of its expansions
are thus difficult unpleasant tasks.  (I speak from experience.)

cond* looks like being better on the first point, and might well be
better on the second.  Those putative advantages would be lost by basing
cond*'s implementation on pcase.

>         Stefan

-- 
Alan Mackenzie (Nuremberg, Germany).



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

* Re: Code for cond*
  2024-02-25 17:22                     ` Alan Mackenzie
@ 2024-02-25 17:46                       ` Alfred M. Szmidt
  2024-02-25 18:13                         ` Stefan Monnier
  0 siblings, 1 reply; 134+ messages in thread
From: Alfred M. Szmidt @ 2024-02-25 17:46 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: monnier, rms, emacs-devel


   Hello, Stefan.

   On Sun, Feb 25, 2024 at 10:38:46 -0500, Stefan Monnier wrote:
   > > On a slightly different topic, when will cond* be added/commited to
   > > GNU Emacs?  Seeing that how cond* works, and documentation has
   > > slightly materialized, it would be good to get some milage.

   I agree.

   > FWIW, I'm opposed to adding it without first unifying the Pcase and
   > `match*` pattern languages (including making sure that third party
   > packages which define their own patterns don't need to define them
   > separately for `pcase` and for `match*`).

   > [ It's trivial to do by making `match*` reuse the Pcase machinery, so
   >   there is no serious obstacle for that.  ]

   I hope that cond* won't be written on top of pcase.  The pcase machinery
   is almost entirely undocumented (and even the "almost" might be an
   exaggeration), and that doesn't look like changing any time soon.

   Debugging pcase itself, or a backtrace involving one of its expansions
   are thus difficult unpleasant tasks.  (I speak from experience.)

I'm sitting on the fence on this (since I don't have to bother with
the complicated expansions from pcase) -- but from a quick glance, the
code for cond* is much easier to grok than pcase, for similar reasons
above but also just how the code is written.

   cond* looks like being better on the first point, and might well be
   better on the second.  Those putative advantages would be lost by basing
   cond*'s implementation on pcase.

Since cond* is much easier to understand, I hope that in a few years
the usages of pcase will vaniahs slowly but surely .. and maybe even
that that pcase could be rewritten using cond*.



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

* Re: Code for cond*
  2024-02-25 17:46                       ` Alfred M. Szmidt
@ 2024-02-25 18:13                         ` Stefan Monnier
  0 siblings, 0 replies; 134+ messages in thread
From: Stefan Monnier @ 2024-02-25 18:13 UTC (permalink / raw)
  To: Alfred M. Szmidt; +Cc: Alan Mackenzie, rms, emacs-devel

>    > FWIW, I'm opposed to adding it without first unifying the Pcase and
>    > `match*` pattern languages (including making sure that third party
>    > packages which define their own patterns don't need to define them
>    > separately for `pcase` and for `match*`).
>
>    > [ It's trivial to do by making `match*` reuse the Pcase machinery, so
>    >   there is no serious obstacle for that.  ]
>
>    I hope that cond* won't be written on top of pcase.

[ Side note: my comment above specifically refers to `match*`,
  not to `cond*`.  Let's not confuse the two: `cond*` is an actual
  addition, whereas `match*` mostly reinvents Pcase patterns, with a few
  tweaks to some of its syntax.  ]

I don't have a strong opinion on that, actually.

I hope that we don't end up having to maintain two implementations of
the pattern matching code, but there can be good arguments for such
a duplication (there are several different implementation strategies,
each with their own advantages), so if that's what it takes, I'm OK
with it.

> and maybe even that that pcase could be rewritten using cond*.

As already mentioned in this long thread, the current `match*`
implementation does not generate good enough code for existing `pcase`
uses (and fixing that probably requires non-trivial changes).


        Stefan




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

* Re: Code for cond*
  2024-02-25 17:10                 ` Stefan Monnier
@ 2024-02-27  3:11                   ` Richard Stallman
  0 siblings, 0 replies; 134+ messages in thread
From: Richard Stallman @ 2024-02-27  3:11 UTC (permalink / raw)
  To: Stefan Monnier; +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. ]]]

  > That's why I suggest the use of "," as in (< ,x y) which makes the
  > difference more clear (and leans on the use of , in the backquote
  > patterns where it similarly indicates places where a new variable is
  > bound).

I'm willing to go along with this if others like the change.
I wonder whether it is easy or hard to implement -- I don't know.

I would like to commit cond* to master without waiting longer.  That
way, bug fixes could be made in master and appear in the log.  Someone
could implement this change, too.  And having it in master
means that people could install changes to actually use it, in code
that people will actually run.


-- 
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] 134+ messages in thread

* Re: Code for cond*
  2024-02-25 15:03                             ` Stefan Monnier
@ 2024-02-29  3:50                               ` Richard Stallman
  2024-02-29 18:07                                 ` Stefan Monnier
  2024-02-29  3:50                               ` Richard Stallman
  1 sibling, 1 reply; 134+ messages in thread
From: Richard Stallman @ 2024-02-29  3:50 UTC (permalink / raw)
  To: Stefan Monnier; +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. ]]]

  > - Which example was that?  Have you found many others?

Sorry, I don't seem to have a copy of it I can find.
I looked for it in my mail archive about this issue
but did not see it.  Mayne what I was sent was function names
and I looked at them in the source code.

I think someoene sent me a list of examples, in December perhaps,
to show me how pcase is often used.  Naybe the items in that list
were function names, which would explain why I can't search for that email.

  > - What do you reply to my own experience that we often want to accept
  >   not only longer lists but also shorter lists?

Maybe you are right, but I don't think I have seen an example of it.
Hmm, was this the purpose of proposing the cl-lambda pattern operator?

I agree it could make sense to combine these wo features ino one,
if there is a clean way to do it  There oould be two flags:

* Ignore excess list elements.  That is what I implemented as
  (cdr-safe ...).

* Accept shorter lists.  That could be called (truncate-ok ...).
(truncate-ok `(foo ,a ,b)) would match (foo lose quux) or
(foo lose) or just (foo).

These two flags are opposutes in spirit, but they do not conflict; it
would make sense to set both flags at once.

   (cdr-safe (truncate-ok `(foo ,a ,b)))

Another possible approach is to specify minimum and maximum list
length for the data to be matched.

   (list-length 1 nil `(foo ,a ,b))

where nil means "no maximum length".

However, we would not want `list-length' to affect sublists.
Nor '`truncate-ok'.  Basically, I think allowing a shorter list
is something that I think would be desirable for one level of list structure
whereas `cdr-safe' seemed to make sense for nested levels.



-- 
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] 134+ messages in thread

* Re: Code for cond*
  2024-02-25 15:03                             ` Stefan Monnier
  2024-02-29  3:50                               ` Richard Stallman
@ 2024-02-29  3:50                               ` Richard Stallman
  1 sibling, 0 replies; 134+ messages in thread
From: Richard Stallman @ 2024-02-29  3:50 UTC (permalink / raw)
  To: Stefan Monnier; +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. ]]]

  > - As you say for "one specific list, it is hard to find anything simpler
  >   than `. ,_'", yet you added `cdr-safe` which applies to a single
  >   pattern, and thus typically to a single list, unless the pattern is
  >   a big (or ...) pattern which are rather unusual.

I can't follow that.  Why do you say "typically to a sigle list"?  I
don't think that is true -- it applies to all sublists of the pattern
used within it.

Bu I suspect a miscommunication.

-- 
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] 134+ messages in thread

* Re: Code for cond*
  2024-02-29  3:50                               ` Richard Stallman
@ 2024-02-29 18:07                                 ` Stefan Monnier
  0 siblings, 0 replies; 134+ messages in thread
From: Stefan Monnier @ 2024-02-29 18:07 UTC (permalink / raw)
  To: Richard Stallman; +Cc: emacs-devel

> Maybe you are right, but I don't think I have seen an example of it.
> Hmm, was this the purpose of proposing the cl-lambda pattern operator?

No, see:

    (defmacro byte-optimize--pcase (exp &rest cases)
      ;; When we do
      ;;
      ;;     (pcase EXP
      ;;       (`(if ,exp ,then ,else) (DO-TEST))
      ;;       (`(plus ,e2 ,e2)        (DO-ADD))
      ;;       (`(times ,e2 ,e2)       (DO-MULT))
      ;;       ...)
      ;;
      ;; we usually don't want to fall back to the default case if
      ;; the value of EXP is of a form like `(if E1 E2)' or `(plus E1)'
      ;; or `(times E1 E2 E3)', instead we either want to signal an error
      ;; that EXP has an unexpected shape, or we want to carry on as if
      ;; it had the right shape (ignore the extra data and pretend the missing
      ;; data is nil) because it should simply never happen.
      ;;
      ;; The macro below implements the second option by rewriting patterns
      ;; like `(if ,exp ,then ,else)'
      ;; to   `(if . (or `(,exp ,then ,else) pcase--dontcare))'.
      ;;
      ;; The resulting macroexpansion is also significantly cleaner/smaller/faster.
      [...])

in `byte-opt.el`.

> Basically, I think allowing a shorter list is something that I think
> would be desirable for one level of list structure whereas `cdr-safe'
> seemed to make sense for nested levels.

I can't think of places where it would make sense to apply something
like `cdr-safe` to nested lists, so it's hard for me to judge.

>   > - As you say for "one specific list, it is hard to find anything simpler
>   >   than `. ,_'", yet you added `cdr-safe` which applies to a single
>   >   pattern, and thus typically to a single list, unless the pattern is
>   >   a big (or ...) pattern which are rather unusual.
> I can't follow that.  Why do you say "typically to a single list"?  I
> don't think that is true -- it applies to all sublists of the pattern
> used within it.

Most patterns have a single list.  As you see in `byte-optimize--pcase`,
I need that kind of functionality for most/all branches.


        Stefan




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

end of thread, other threads:[~2024-02-29 18:07 UTC | newest]

Thread overview: 134+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-01-18  3:37 Code for cond* Richard Stallman
2024-01-18  4:59 ` Emanuel Berg
2024-01-20  3:39   ` Richard Stallman
2024-01-24 12:37     ` Po Lu
2024-01-24 19:12     ` Alan Mackenzie
2024-01-27  3:35       ` Richard Stallman
2024-01-18 15:44 ` Andrea Corallo
2024-01-19 10:42   ` João Távora
2024-01-21  3:04     ` Richard Stallman
2024-01-21 20:05       ` Adam Porter
2024-01-22  5:32         ` tomas
2024-01-23 13:39         ` Richard Stallman
2024-01-24  6:02           ` Po Lu
2024-01-24  9:48       ` Stefan Kangas
2024-01-24 10:09         ` Emanuel Berg
2024-01-24 11:30         ` João Távora
2024-01-24 12:08           ` João Távora
2024-01-24 12:09         ` Po Lu
2024-01-24 12:22           ` Ihor Radchenko
2024-01-24 12:33             ` Po Lu
2024-01-24 13:34               ` Ihor Radchenko
2024-01-24 13:52                 ` João Távora
2024-01-24 14:31                   ` Po Lu
2024-01-27  3:35                   ` Richard Stallman
2024-01-27  3:35                   ` Richard Stallman
2024-01-24 14:07                 ` Po Lu
2024-01-24 14:20                   ` Ihor Radchenko
2024-01-24 14:34                     ` Po Lu
2024-01-24 14:44                       ` Ihor Radchenko
2024-01-24 14:47                         ` João Távora
2024-01-24 15:28                         ` Emanuel Berg
2024-01-24 16:30                           ` Ihor Radchenko
2024-01-24 16:34                             ` Emanuel Berg
2024-01-25  9:06                           ` Po Lu
2024-01-25  9:55                             ` Alfred M. Szmidt
2024-01-25 10:38                               ` Eli Zaretskii
2024-01-25 11:29                               ` Po Lu
2024-01-25 12:04                                 ` Emanuel Berg
2024-01-25 13:32                                   ` Po Lu
2024-01-25 14:08                                     ` Emanuel Berg
2024-01-25 13:19                                 ` Alfred M. Szmidt
2024-01-25 13:43                                   ` Emanuel Berg
2024-01-25 14:31                                     ` Eli Zaretskii
2024-01-25 15:02                                       ` Emanuel Berg
2024-01-25 15:29                                         ` Eli Zaretskii
2024-01-25 15:33                                           ` Alfred M. Szmidt
2024-01-25 15:50                                             ` Eli Zaretskii
2024-01-25 16:01                                               ` Alfred M. Szmidt
2024-01-25 16:13                                                 ` Eli Zaretskii
2024-01-25 15:40                                           ` Emanuel Berg
2024-01-25 10:30                             ` Eli Zaretskii
2024-01-25 11:27                               ` Emanuel Berg
2024-01-24 12:15         ` Alan Mackenzie
2024-01-24 12:28           ` Emanuel Berg
2024-01-25  9:10           ` Po Lu
2024-01-25 11:56             ` Emanuel Berg
2024-01-25 13:21               ` Po Lu
2024-01-25 13:56                 ` Emanuel Berg
2024-01-25 23:32           ` Stefan Kangas
2024-01-20  3:39   ` Richard Stallman
2024-01-23 18:10 ` Stefan Monnier via Emacs development discussions.
2024-01-24  4:49   ` JD Smith
2024-01-24  9:45     ` Stefan Kangas
2024-01-24 15:29       ` JD Smith
2024-01-24 15:55         ` Stefan Monnier
2024-01-24 16:02           ` Stefan Monnier
2024-01-24 16:20           ` JD Smith
2024-01-24 17:08             ` Stefan Monnier
2024-01-24 16:35           ` [External] : " Drew Adams
2024-01-24 16:30     ` Drew Adams
2024-02-01  8:56       ` Madhu
2024-02-01 22:46         ` Emanuel Berg
2024-01-25  3:16     ` Madhu
2024-01-25 13:57       ` Stefan Monnier
2024-01-25 15:17         ` JD Smith
2024-01-25 15:37           ` JD Smith
2024-01-25 15:44             ` Alfred M. Szmidt
2024-01-25 16:00               ` JD Smith
2024-01-25 16:05               ` Stefan Monnier
2024-01-25 16:12                 ` Alfred M. Szmidt
2024-01-25 16:20                   ` Stefan Monnier
2024-01-25 16:33                   ` JD Smith
2024-01-29  3:19             ` Richard Stallman
2024-01-26  4:30   ` Richard Stallman
2024-01-28  3:06     ` Stefan Monnier via Emacs development discussions.
2024-01-30  3:59       ` Richard Stallman
2024-01-30 13:02         ` Stefan Monnier
2024-02-23  3:04           ` Richard Stallman
2024-01-26  4:30   ` Richard Stallman
2024-01-28  4:16     ` Stefan Monnier via Emacs development discussions.
2024-01-31  3:32       ` Richard Stallman
2024-01-31 13:20         ` Stefan Monnier
2024-02-03  3:32           ` Richard Stallman
2024-02-03  6:09             ` Stefan Monnier
2024-02-03  6:48               ` Emanuel Berg
2024-02-04  4:46               ` Richard Stallman
2024-02-04 14:04                 ` Stefan Monnier
2024-02-04  4:46               ` Richard Stallman
2024-02-04 13:58                 ` Stefan Monnier
2024-02-13  0:48                 ` Stefan Monnier
2024-02-13  2:27                   ` Stefan Monnier
2024-02-14 11:16                   ` Richard Stallman
2024-02-14 12:45                     ` Stefan Monnier
2024-02-22  3:05                       ` Richard Stallman
2024-02-22  4:08                         ` Stefan Monnier
2024-02-25  3:14                           ` Richard Stallman
2024-02-25 15:03                             ` Stefan Monnier
2024-02-29  3:50                               ` Richard Stallman
2024-02-29 18:07                                 ` Stefan Monnier
2024-02-29  3:50                               ` Richard Stallman
2024-02-13  0:41         ` Stefan Monnier
2024-02-23  3:04           ` Richard Stallman
2024-02-23 13:39             ` Stefan Monnier
2024-02-25  3:16               ` Richard Stallman
2024-02-25 14:57                 ` Alfred M. Szmidt
2024-02-25 15:38                   ` Stefan Monnier
2024-02-25 16:42                     ` Alfred M. Szmidt
2024-02-25 17:13                       ` Stefan Monnier
2024-02-25 17:22                     ` Alan Mackenzie
2024-02-25 17:46                       ` Alfred M. Szmidt
2024-02-25 18:13                         ` Stefan Monnier
2024-02-25 17:10                 ` Stefan Monnier
2024-02-27  3:11                   ` Richard Stallman
2024-01-24 12:39 ` Alan Mackenzie
2024-01-24 14:43   ` Emanuel Berg
2024-01-24 16:25   ` Manuel Giraud via Emacs development discussions.
2024-01-25 14:01   ` Code for cond* - cond*-match, cond*-subpat and backtrack-aliases Alan Mackenzie
2024-01-29  3:19     ` Richard Stallman
2024-01-29  8:54       ` Andreas Schwab
2024-01-29  3:19     ` Richard Stallman
2024-01-29 12:16       ` JD Smith
2024-02-01  3:51         ` Richard Stallman
2024-02-01 14:54           ` JD Smith
2024-02-04  4:42             ` Richard Stallman

Code repositories for project(s) associated with this external index

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

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.