0

I've got this

(defun testor (s)
  (if (or (< s 1) (> s 21)) (message "Bad")
    (message "good")))

which works. But a similar

(defun test2 (n)
  (pcase n
    ((pred (< n 1)) (message "bad"))
    ((pred (> n 21)) (message "bad"))
    (n (message "%s" n))))

gives

(test2 0)
"0"

i.e., it's not flagging 0 as less than 1. What am I missing here? I'm on 26.2 and am in a emacs -q session.

Drew
  • 75,699
  • 9
  • 109
  • 225
147pm
  • 2,907
  • 1
  • 18
  • 39

2 Answers2

1

But why doesn't pred work?

Because pred passes the value being matched as the last argument to its predicate.

Could you explain a bit more, maybe contrasting examples?

Let me start by quoting the current docstring of pcase in Emacs 27:

pcase is a Lisp macro in `pcase.el'.

(pcase EXP &rest CASES)

  Probably introduced at or before Emacs version 24.1.

Evaluate EXP to get EXPVAL; try passing control to one of CASES.
CASES is a list of elements of the form (PATTERN CODE...).
For the first CASE whose PATTERN "matches" EXPVAL,
evaluate its CODE..., and return the value of the last form.
If no CASE has a PATTERN that matches, return nil.

Each PATTERN expands, in essence, to a predicate to call
on EXPVAL.  When the return value of that call is non-nil,
PATTERN matches.  PATTERN can take one of the forms:

  _                matches anything.
  'VAL             matches if EXPVAL is `equal' to VAL.
  KEYWORD          shorthand for 'KEYWORD
  INTEGER          shorthand for 'INTEGER
  STRING           shorthand for 'STRING
  SYMBOL           matches anything and binds it to SYMBOL.
                   If a SYMBOL is used twice in the same pattern
                   the second occurrence becomes an `eq'uality test.
  (pred FUN)       matches if FUN called on EXPVAL returns non-nil.
  (app FUN PAT)    matches if FUN called on EXPVAL matches PAT.
  (guard BOOLEXP)  matches if BOOLEXP evaluates to non-nil.
  (let PAT EXPR)   matches if EXPR matches PAT.
  (and PAT...)     matches if all the patterns match.
  (or PAT...)      matches if any of the patterns matches.

FUN in `pred' and `app' can take one of the forms:
  SYMBOL  or  (lambda ARGS BODY)
     call it with one argument
  (F ARG1 .. ARGn)
     call F with ARG1..ARGn and EXPVAL as n+1'th argument

FUN, BOOLEXP, EXPR, and subsequent PAT can refer to variables
bound earlier in the pattern by a SYMBOL pattern.

Additional patterns can be defined using `pcase-defmacro'.

See Info node `(elisp) Pattern-Matching Conditional' in the
Emacs Lisp manual for more information and examples.

The Elisp manual section (elisp) pcase Macro expounds a bit on this:

‘(pred FUNCTION)’
     Matches if the predicate FUNCTION returns non-‘nil’ when called on
     EXPVAL.  the predicate FUNCTION can have one of the following
     forms:

     function name (a symbol)
          Call the named function with one argument, EXPVAL.

          Example: ‘integerp’

     lambda expression
          Call the anonymous function with one argument, EXPVAL (see
          Lambda Expressions).

          Example: ‘(lambda (n) (= 42 n))’

     function call with N args
          Call the function (the first element of the function call)
          with N arguments (the other elements) and an additional N+1-th
          argument that is EXPVAL.

          Example: ‘(= 42)’
          In this example, the function is ‘=’, N is one, and the actual
          function call becomes: ‘(= 42 EXPVAL)’.

‘(guard BOOLEAN-EXPRESSION)’
     Matches if BOOLEAN-EXPRESSION evaluates to non-‘nil’.

and gives a few examples:

Example: Using ‘and’
--------------------

A common idiom is to write a pattern starting with ‘and’, with one or
more SYMBOL sub-patterns providing bindings to the sub-patterns that
follow (as well as to the body forms).  For example, the following
pattern matches single-digit integers.

     (and
       (pred integerp)
       n                     ; bind ‘n’ to EXPVAL
       (guard (<= -9 n 9)))

First, ‘pred’ matches if ‘(integerp EXPVAL)’ evaluates to non-‘nil’.
Next, ‘n’ is a SYMBOL pattern that matches anything and binds ‘n’ to
EXPVAL.  Lastly, ‘guard’ matches if the boolean expression ‘(<= -9 n 9)’
(note the reference to ‘n’) evaluates to non-‘nil’.  If all these
sub-patterns match, ‘and’ matches.

In other words, the following:

(pcase x
  ((pred (< y 1)) z))

expands to something like:

(and (< y 1 x) z)

Whereas this:

(pcase x
  ((guard (< y 1)) z))

expands to something like:

(and (< y 1) z)
Basil
  • 12,019
  • 43
  • 69
1

The name "pred" suggests it takes a predicate function, and C-h f pcase says

(pred FUN)       matches if FUN called on EXPVAL returns non-nil.

...

FUN in `pred' and `app' can take one of the forms:
  SYMBOL  or  (lambda ARGS BODY)
     call it with one argument
  (F ARG1 .. ARGn)
     call F with ARG1..ARGn and EXPVAL as n+1'th argument

So FUN in (pred FUN) can take 3 forms:

  1. A function takes one argument and returns nil (False/Don't match) or non-nil (True/Match)
  2. A lambda (similiar to the above function)
  3. A partial funcall

The following uses these 3 forms doing the same thing, i.e., check if you entered a string

;; 1. function
(pcase (read)
  ((pred stringp) "a string"))

;; 2. lambda
(pcase (read)
  ((pred (lambda (x) (stringp x))) "a string"))

;; 3. funcall
(pcase (read)
  ((pred (stringp)) "a string"))

Back to your question, with pred the easiest form is the third form:

(pcase n
  ((pred (> 1)) (message "bad"))
  ((pred (< 21)) (message "bad"))
  (_ (message "%s" n)))

In above, when using the third from, partial funcall, n must be the last argument

(< n 1) => (> 1 n) => (pred (> 1))
(> n 21) => (< 21 n) => (pred (< 21))

By the way, don't forget using M-x emacs-lisp-macroexpand to check your pcase use.

xuchunyang
  • 14,302
  • 1
  • 18
  • 39