help-gnu-emacs
[Top][All Lists]
Advanced

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

RE: [External] : Re: Emacs 30.0 warning from `cl-pushnew' and `memql'


From: Drew Adams
Subject: RE: [External] : Re: Emacs 30.0 warning from `cl-pushnew' and `memql'
Date: Fri, 30 Dec 2022 07:29:38 +0000

> > I think you'll need to explain your "incorrect 
> > use of `quote' in `cl-case'".
> 
> I did at the very beginning:
> (case FOO ('a (do-a)) ('b (do-b) ...))
> that's an incorrect use of quote, and such incorrect uses of quote in
> `cl-case` are very frequent and have been since long before `pcase`
> existed.

Oh, so it's not that `case' somehow uses `quote'
incorrectly.  It's that some users use `case'
incorrectly.  I thought you were trying to say
something else, sorry.

I've never seen anyone misuse the `case' syntax
that way, but I'm sure it's possible.

But "very frequent", really?  Any evidence that
that's been the case for the many users of CL
`case' since the mid-80s?

How about comparing that user mistake with user
confusion about `pcase'?  Including its use to
handle the simple `case' use case.

[BTW, you still haven't shown the simple `case'
case for `pcase', i.e., correspondences to
`case' examples - e.g., those shown here so far.
Weren't you planning to show how much simpler
it is to use `pcase' for that use case than it
is to use `case'?  Let's see apples-to-apples.]

But assuming you're right, do you see only
Elisp users having this misunderstanding, or
have you seen CL users also confused this way?

Maybe it's just that the Emacs description has
some room for improvement.  Seems like it
should be clear enough to say that you give it
lists of values and `case' tries to match,
using `eql', the values in those lists against
the result of evaluating its first arg.

The point of `case' is to (1) evaluate a sexp
only once and (2) compare that value against
other values, in a certain order, using `eql'.

(With `cond' you need to (1) use `let' to bind
the result of the sexp evaluation and (2) use
`eql' or `memql' explicitly over and over.)

And maybe it should say that each such list,
and the values in it, are presented _as such_
in the syntax - not evaluated.  E.g., not
'(X Y Z), ('X 'Y 'Z), or '('X 'Y 'Z) - just
plain (X Y Z).  IOW, as simple as it gets.

And that, for any literal values X, Y, and Z,
including, yes, the symbols nil and t, and
nil's synonym ().

(Of course, since the predicate is `eql' it
generally doesn't make much sense to use
conses or atoms such as strings and vectors as
the list elements - they won't be matched.
But symbols, numbers, chars - such atoms like
`eql' well enough.  They're the use case for
`case'.)

The Emacs doc string for `cl-case' talks about
evaluating and evaluating and evaluating...
But it doesn't ever say that the values in
KEYLIST are _NOT_ evaluated.  Without that, I
can see how someone reading that doc string
might not get that that's the case.
`C-h f cl-case':

 "Eval EXPR and choose among clauses on that value.

[Clauses on a value?  (Maybe it means choose
"based" on that value.)]

 "Each clause looks like (KEYLIST BODY...).
  EXPR is evaluated and compared against each
  key in each KEYLIST; the corresponding BODY
  is evaluated.

[Where "corresponding" means corresponding to
a clause with a key that is actually `eql'...]

 "If no clause succeeds, cl-case returns nil.
  A single non-nil atom may be used in place
  of a KEYLIST of one atom.  A KEYLIST of t
  or `otherwise' is allowed only in the final
  clause, and matches if no other keys match.
  Key values are compared by `eql'."

[It should say that any clauses after the use
of KEYLIST `t' or `otherwise' are just ignored.
If they were really "allowed only in the final
clause" then any clause after them would raise
an error instead of being ignored.]

[And actually, what about just (KEYLIST)?
Does that fit the template (KEYLIST BODY...)?
(KEYLIST) acts the same as (KEYLIST nil) - it
returns nil if one of its keys matches.  But
does "BODY..." mean that BODY can be missing
altogether?  I can never remember the Emacs
doc convention for this.]

Anyway...

. Eval EXPR. - check!
. EXPR is evaluated. - check!

. The value of EXPR is compared against each
  key in each KEYLIST. - Whoa! What's a key?
  Just what are those things in KEYLIST?  Is
  each one a sexp that gets _evaluated_?
  No say nada.  Bzzzzt!

. The corresponding BODY is evaluated. - check!

. Key matching - Whoa!  What's that?

. Key values are compared with `eql'.  Hm, that
  might be understandable, if we knew what "key
  values" were.  Does it mean the result of
  evaluating a key, i.e., a list element?  Or
  does it just mean a key - a list element?

The thing that seems to be missing might well
be at the root of the "very frequent" mistake
you report, no?

What's missing here?  Keys are _literal values_
to be compared ("matched") using `eql'.  They
are not sexps to be evaluated.  Seems pretty
important to point that out.

Now what about the Emacs CL manual (node
Conditionals) - is it any better?

 "This macro evaluates KEYFORM, then compares
  it with the key values listed in the various
  CLAUSEs."

[It doesn't compare KEYFORM with the values.
It compares the result of evaluating KEYFORM
with the values.  But maybe that's a nit.]

 "Whichever clause matches the key is executed;
  comparison is done by 'eql'."

What does it mean for a _clause_ to match a
key (a value in the KEYLIST of the clause)?

 "The clauses are of the form
  (KEYLIST BODY-FORMS...) where KEYLIST is a
  list of key values."

A bit better than the doc string.  KEYLIST "is"
a list of values, not sexps to be evaluated.
And KEYLIST is a (literal) list, not a sexp to
be evaluated to a list.  Maybe users getting
confused would be better off reading the CL
manual than the doc string?
___

Now let's see.  What does CLTL2 tell us?

https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node84.html

 "If KEY is in the KEYLIST (that is, is `eql'
  to any item in the KEYLIST)..."

So KEY is something in KEYLIST (which is a list
presumably).  So it's a list element.

 "The keys in the keylists are _not_ evaluated;
  literal key values must appear in the keylists."

Emphasis on "not".  Literal values.  Bingo -
pretty clear!  (It might also have said that
KEYLIST is a literal list, not a sexp that's
evaluated to give a list.)

BTW, there's also this bit:

 "It is an error for the same key to appear in
  more than one clause; a consequence is that
  the order of the clauses does not affect the
  behavior of the case construct."

Apparently Emacs's `cl-case' doesn't raise such
an error.  (To emulate CL `case' it should.)

To me, the CLTL2 doc is pretty clear about the
keys and the keylists not being evaluated.  This
gives me the idea that maybe Elisp users get
confused about this and CLTL2 readers don't get
confused in the way you reported.  A hypothesis.
___

Coming back to the Elisp manual, `cl-case' seems
to be mentioned only in the doc about `pcase'.
(BTW, `cl-case' isn't in the Index, alas.)

There we also say this, which I don't understand,
in the section where it compares `pcase' with
`cl-case'.  Dunno whether it's that (1) I don't
understand the English or (2) what it says is
wrong.

It shows a `pcase' example which starts with
this: (pcase (get-return-code x) ...

 "With 'cl-case', you would need to explicitly
  declare a local variable 'code' to hold the
  return value of 'get-return-code'."

Huh?  Does that mean you need to use a variable
as the first arg to `cl-case'?  If so, that's
not right.

(defun get-return-code (arg)
  (if (< arg 42) 'success 'failure))

(let ((x  13))
  (cl-case (get-return-code x)
    (success (message "Done!"))
    (t       (message "Unknown!"))))

 ==> Done!

And with x bound to 56, ==> Unknown!

We didn't "declare a local variable ... to
hold the return value of `get-return-code'."

Dunno what it means to "declare" a variable in
Elisp (except a vacuous defvar: (defvar x)),
but a guess is that it's trying to say you need
to _bind_ a local var to the value returned by
(get-return-code x), and then use that variable
as the first arg to `cl-case'.  If so, that's
not true.

It's the value returned from evaluating the
sexp (get-return-code x) that `cl-case' tries
to match (using `eql') against symbol `success'.
`cl-case' doesn't need its first arg to be a
"local variable".

Let me know what I'm missing here.

<<attachment: winmail.dat>>


reply via email to

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