From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Richard Stallman Newsgroups: gmane.emacs.devel Subject: Re: cond* Date: Thu, 28 Dec 2023 22:52:58 -0500 Message-ID: References: <87frzuae9n.fsf@posteo.net> <871qbatqc8.fsf@posteo.net> <87wmsz7lzn.fsf@posteo.net> Reply-To: rms@gnu.org Content-Type: text/plain; charset=Utf-8 Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="633"; mail-complaints-to="usenet@ciao.gmane.io" Cc: emacs-devel@gnu.org To: Philip Kaludercic Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Fri Dec 29 04:53:25 2023 Return-path: Envelope-to: ged-emacs-devel@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1rJ3w4-000AUc-S2 for ged-emacs-devel@m.gmane-mx.org; Fri, 29 Dec 2023 04:53:25 +0100 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1rJ3vi-0001Y9-PU; Thu, 28 Dec 2023 22:53:02 -0500 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rJ3vf-0001Xb-IK for emacs-devel@gnu.org; Thu, 28 Dec 2023 22:52:59 -0500 Original-Received: from fencepost.gnu.org ([2001:470:142:3::e]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rJ3vf-00055X-6b; Thu, 28 Dec 2023 22:52:59 -0500 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org; s=fencepost-gnu-org; h=Date:References:Subject:In-Reply-To:To:From: mime-version; bh=k1HKR5Oslqd7ScZ5hqmLe9ZoTwRT496p71mbDvFAm/4=; b=lvNYRJvdK/HE JwGLEyEi+oI8MbM8AEqFR8DFVXfTD5PGPtmtM92xwYSdJCyY0dII/wEF1Mco2SqciDlMYxpOQRISE ytfXi8sah8Rk0Mlcf8TjNDeP3bHcaxdVaf/lDL378S303KMiX7wC+Jfq0nKpZP+v3QsbY7/wsGOXx pdTVscLVg2KgAUKZuVuInCrP5gDTD3JcUyuIDqSl70hovVWJRV+NCRoCdWRYmr2AwuLpWvFKkj737 6v4eXdbWQ5Bzw+agtQEqz3NjBxjHiYf9M8DO7w0GKVGGlI6pt3/75d4tp3L1FeigDx9NSrteqy8Md bVZ8PqCfRZMrWJxdggHc1Q==; Original-Received: from rms by fencepost.gnu.org with local (Exim 4.90_1) (envelope-from ) id 1rJ3ve-0005k5-TI; Thu, 28 Dec 2023 22:52:58 -0500 In-Reply-To: <87wmsz7lzn.fsf@posteo.net> (message from Philip Kaludercic on Wed, 27 Dec 2023 14:34:20 +0000) X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.devel:314303 Archived-At: [[[ 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 include below the latest version. Compare the total complexity of specification of cond* with the total complexity of specification of pcase. Then likewise compare the generality and flexibility of cond* with pcase. Each clause in cond* that has just one element -- no "clause body" -- is a "non-exit" clause. After that clause completes, control always proceeds to the following clause. All other clauses are like cond clauses, in that when the condition is true, the 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. (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) **Possible types of patterns for match*. CONSTANT: nil, t, a keyword, a quoted constant other than a cons cell, 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. 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 match-nil 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. (cdr-safe QUOTED-CONS-CELL) This pattern is equivalent to QUOTED-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. (cdr QUOTED-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 backquoted 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 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. 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. xxxxxxxxxxxxxx not decided yet This would give some added power, but does that power really enable the user to do more with cond*? I am not sure. PATTERN can also be a list, which is a pattern to destructure, For instance, (plistp `(,a ,b . ,rest)) checks that VA num-foos 1) ; Match any number greater than 1, bind `num-foos' to it. (> num-foos min-foos) ; Match any number greater than MIN-FOOS, ; bind `num-foos' to it. 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. Constrained variable constructs can be nested: For example, (integerp (> num-foos 1)) ; Match any integer > 1, ; bind `num-foos' to it. nests the constrained pattern (integerp num-foos) inside the constrained pattern (< ... 1). This nested constrained pattern first tests that the value is greater than 1, then tests it is an integer. If both of those constraints are true, it setse `num-foos'. (> (integerp num-foos) 1) ; Match any integer > 1, ; bind `num-foos' to it. does the two tests in the opposite order; it is equivalent, because an error in the call to `>' only makes this pattern fail to match. Advanced hack for experts: the OTHER-ARGS can refer to variables bound earlier in this pattern-matching operation. For example, `(,(integerp smaller) ,(> (integerp bigger) smaller)) ; Matches a list of two integers in which the second integer ; is larger than the first. (or `(,(integerp smaller) ,(> (integerp bigger) smaller)) `(,(integerp bigger) ,(< (integerp smaller) bigger))) ; matches a ist of two integers and binds `bigger' to the bigger one ; and `smaller' to the smaller one. However, it might be cleaner to match the two integers regardless of their numberical ordering, then in Lisp code see which one is larger. 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. 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 These three ways of using `cond*' are worth noting. (cond* ((match* PATTERN VALUE) TEST)) PATTERN is a pattern as described above. For instance (match `(+ (expt ,_ ,_)) (get-complex-list)) would decompose the result of (get-complex-list) and check for `+' and `expt' in it. PATTERN will not be evaluated, just used as guidance when the `match' form is macroexpanded. If PATTERN includes substitutions, it will virtually perform those substitutions and compare parts of VALUE against them. (match `(foo ,foo) x) compares (car x) with `foo'. TEST is a Lisp expression to be evaluated if VALUE seems to fit PATTERN. If TEST evaluates non-nil, the match succeeds. `match' returns t if it succeeds in matching, otherwise nil. `match' can also handle vectors and maybe some other kinds of structures. (cond* ((match-set* PATTERN VALUE))) PATTERN is a pattern as described above. For instance (match-set `(+ ,y ,z) (get-complex-list)) would decompose the result of (get-complex-list), verify the car, and set y and z. It is equivalent to this: (if (eq (car value) '+) (progn (setq y (car (cadr value)) z (cadr (cadr value))) TEST)) PATTERN will not be evaluated, just used as guidance when the `match-set' form is macroexpanded, producing code like this: TEST is a Lisp expression to be evaluated if VALUE seems to fit PATTERN. If TEST evaluates non-nil, the match succeeds. `match-set' returns t if it succeeds in matching, otherwise nil. `match-set' can also handle vectors and maybe some other kinds of structures. (cond* ((match* PATTERN VALUE) BODY...)) Like the previous, except that instead of setting the variables found in PATTERN, it let-binds them and runs BODY with those bindings. -- 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)