all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
* EXPVAL in pcase-defmacro docstrings
@ 2018-05-30 12:28 Stefan Monnier
  2018-05-31 14:56 ` Thien-Thi Nguyen
  0 siblings, 1 reply; 3+ messages in thread
From: Stefan Monnier @ 2018-05-30 12:28 UTC (permalink / raw)
  To: Thien-Thi Nguyen; +Cc: emacs-devel


I think this change is misguided:

- When reading source code, you get things like:

      (pcase-defmacro radix-tree-leaf (vpat)
        "Build a `pcase' pattern that matches radix-tree leaf EXPVAL.
      VPAT is a `pcase' pattern to extract the value."

  where the EXPVAL comes out of nowhere

- The wording "matches radix-tree leaf EXPVAL" makes it sound like the
  reader is already familiar with EXPVAL and that we know already that
  it's a radix-tree leaf, whereas the pattern will have to check if
  EXPVAL is such a leaf.

- In the pcase docstring, EXPVAL refers to the "toplevel" value, so when
  we say things like

      'VAL             matches if EXPVAL is `equal' to VAL.

  it makes it sound like

      (cons '1 '2)

  will not match against the value (1 . 2) because EXPVAL is (1 . 2) and
  '1 is not `equal` to (1 . 2).

- The "standard" way to describe a pattern is to say things like

      (fumble-tree E1 E2) matches fumble-trees

  E.g.

      _ matches anything
      SYMBOL           matches anything and binds it to SYMBOL.
      (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.

  While it may occasionally be handy to be able to refer to EXPVAL,
  for example in

      (app FUN PAT)    matches if FUN called on EXPVAL matches PAT.

  I believe the standard terminology is to say "the target" instead of
  EXPVAL.

For example, I think the patch below would improve the doc.


        Stefan


diff --git a/lisp/emacs-lisp/radix-tree.el b/lisp/emacs-lisp/radix-tree.el
index 2491ccea95..6a11977782 100644
--- a/lisp/emacs-lisp/radix-tree.el
+++ b/lisp/emacs-lisp/radix-tree.el
@@ -196,8 +196,8 @@ radix-tree-prefixes
 
 (eval-and-compile
   (pcase-defmacro radix-tree-leaf (vpat)
-    "Build a `pcase' pattern that matches radix-tree leaf EXPVAL.
-VPAT is a `pcase' pattern to extract the value."
+    "Pattern which matches a radix-tree leaf.
+The pattern VPAT is matched against the leaf's carried value."
     ;; FIXME: We'd like to use a negative pattern (not consp), but pcase
     ;; doesn't support it.  Using `atom' works but generates sub-optimal code.
     `(or `(t . ,,vpat) (and (pred atom) ,vpat))))

diff --git a/lisp/emacs-lisp/pcase.el b/lisp/emacs-lisp/pcase.el
index fa7b1de8b4..0ba0d75776 100644
--- a/lisp/emacs-lisp/pcase.el
+++ b/lisp/emacs-lisp/pcase.el
@@ -119,7 +119,7 @@ pcase
 PATTERN matches.  PATTERN can take one of the forms:
 
   _                matches anything.
-  \\='VAL             matches if EXPVAL is `equal' to VAL.
+  \\='VAL             matches objects `equal' to VAL.
   KEYWORD          shorthand for \\='KEYWORD
   INTEGER          shorthand for \\='INTEGER
   STRING           shorthand for \\='STRING



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

* Re: EXPVAL in pcase-defmacro docstrings
  2018-05-30 12:28 EXPVAL in pcase-defmacro docstrings Stefan Monnier
@ 2018-05-31 14:56 ` Thien-Thi Nguyen
  2018-06-04 14:43   ` Stefan Monnier
  0 siblings, 1 reply; 3+ messages in thread
From: Thien-Thi Nguyen @ 2018-05-31 14:56 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

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


() Stefan Monnier <monnier@IRO.UMontreal.CA>
() Wed, 30 May 2018 08:28:29 -0400

   I think this change is misguided:

   - When reading source code, you get things like:

         (pcase-defmacro radix-tree-leaf (vpat)
           "Build a `pcase' pattern that matches radix-tree leaf EXPVAL.
         VPAT is a `pcase' pattern to extract the value."

     where the EXPVAL comes out of nowhere

   - The wording "matches radix-tree leaf EXPVAL" makes it sound
     like the reader is already familiar with EXPVAL and that we
     know already that it's a radix-tree leaf, whereas the
     pattern will have to check if EXPVAL is such a leaf.

Yes.  That is awkward, when reading the source code.  Thanks for
pointing it out; i was focusing on the aggregated docstring for
that change.  For docstrings where it makes sense to use EXPVAL
(maybe not this one), how about we add a small comment pointing
to the EXPVAL convention?  That way, we retain consistency in
the presentation (for ‘C-h f pcase RET’), and give a clue to
people who read source code.

I say "maybe" because, in trying to better understand the
operation of ‘radix-tree-leaf’, i found this comment puzzling:

    ;; FIXME: We'd like to use a negative pattern (not consp),
    ;; but pcase doesn't support it.  Using `atom' works but
    ;; generates sub-optimal code.
    `(or `(t . ,,vpat) (and (pred atom) ,vpat))))

Does that mean we'd want a hypothetical ‘(pred (not consp))’ if
such a construct were supported by ‘pcase’?

Another question i have concerns ‘(t . ...)’.  My understanding
is that in a structural pattern (that begins with backquote), a
symbol represents itself.  However, IIUC the car of the nodes is
always a string.  So this subpattern never matches.

Experimenting in *scratch*, i see:

 (let ((tr radix-tree-empty)
       sub)
   (setq tr (radix-tree-insert tr "all" 0)
         tr (radix-tree-insert tr "abc" 99)
         tr (radix-tree-insert tr "abx" 2)
         sub (radix-tree-subtree tr "a"))
   (list :sub sub
         :etc (pcase sub
                ((radix-tree-leaf v) v))))
 =>
 (:sub
  (("ll" . 0)
   ("b"
    ("c" . 99)
    ("x" . 2)))
  :etc nil)
 
However, if i define ‘radix-tree-leaf-NEW’ as follows:

 (eval-and-compile
   (pcase-defmacro radix-tree-leaf-NEW (vpat)
     `(or `(,_ . ,,vpat)
          (and (pred atom)
               ,vpat))))

replacing ‘t’ w/ ‘,_’ (comma underscore) to "match anything", i
see instead:

 (let ((tr radix-tree-empty)
       sub)
   (setq tr (radix-tree-insert tr "all" 0)
         tr (radix-tree-insert tr "abc" 99)
         tr (radix-tree-insert tr "abx" 2)
         sub (radix-tree-subtree tr "a"))
   (list :sub sub
         :etc (pcase sub
                ((radix-tree-leaf-NEW v) v))))
 =>
 (:sub
  (("ll" . 0)
   ("b"
    ("c" . 99)
    ("x" . 2)))
  :etc
  (("b"
    ("c" . 99)
    ("x" . 2))))

Thinking about this a bit more, i suppose the comment makes
sense if the intention of using ‘t’ is to never match.  Since
‘pcase’ supports lambda expressions for the ‘pred’ pattern,
we can use one directly:

 (pred (lambda (x) (not (consp x))))

and then simplify the definition to something like:

 (eval-and-compile
   (pcase-defmacro radix-tree-leaf-NEW-2 (vpat)
     `(and (pred (lambda (x)
                   (not (consp x))))
           ,vpat)))

Another idea is to make ‘pcase’ support a new core pattern, say
"npred", which would be similar to ‘pred’ but invert the result
of the predicate it uses.  Here, it would be: ‘(npred consp)’.

If this interpretation is correct, i would also suggest another
change: from VPAT to VAR.  This would emphasize that VAR is the
usual vehicle for binding the leaf node "data" for use in the
clause's body forms.  (Maybe "usual" is stretching it -- i found
only one use of ‘radix-tree-leaf’ in the Emacs 26 repo.)  Using
VAR would be easier to document, too.

Anyway, the documentation can say "VAR can also be a pcase
pattern", so as not preclude more complex usage.  WDYT?

I'll respond to the remaining points in the next mail.

-- 
Thien-Thi Nguyen -----------------------------------------------
 (defun responsep (query)               ; (2018) Software Libero
   (pcase (context query)               ;       = Dissenso Etico
     (`(technical ,ml) (correctp ml))
     ...))                              748E A0E8 1CB8 A748 9BFA
--------------------------------------- 6CE4 6703 2224 4C80 7502


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

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

* Re: EXPVAL in pcase-defmacro docstrings
  2018-05-31 14:56 ` Thien-Thi Nguyen
@ 2018-06-04 14:43   ` Stefan Monnier
  0 siblings, 0 replies; 3+ messages in thread
From: Stefan Monnier @ 2018-06-04 14:43 UTC (permalink / raw)
  To: emacs-devel

> Yes.  That is awkward, when reading the source code.  Thanks for
> pointing it out; i was focusing on the aggregated docstring for
> that change.  For docstrings where it makes sense to use EXPVAL
> (maybe not this one), how about we add a small comment pointing
> to the EXPVAL convention?  That way, we retain consistency in
> the presentation (for ‘C-h f pcase RET’), and give a clue to
> people who read source code.

As mentioned, I think "target" is the standard terminology, so we could
use that.  Of course, there might be cases where EXPVAL would work
better, but I don't think we need to encourage people to use EXPVAL nor
to decide how best to use it when it makes sense because it will likely
depend on the specifics.

>     ;; FIXME: We'd like to use a negative pattern (not consp),
>     ;; but pcase doesn't support it.  Using `atom' works but
>     ;; generates sub-optimal code.
>     `(or `(t . ,,vpat) (and (pred atom) ,vpat))))
>
> Does that mean we'd want a hypothetical ‘(pred (not consp))’ if
> such a construct were supported by ‘pcase’?

The idea was to use

     `(or `(t . ,,vpat) (and (not (pred consp)) ,vpat))))

Strictly speaking I'm very happy with the appearance of the current
code, the comment is only about the resulting macroexpansion because
pcase doesn't understand that (atom X) and (consp X) are
mutually exclusive.  But you're right that maybe a halfway solution
would be

     `(or `(t . ,,vpat) (and (pred (not consp)) ,vpat))))

which might be much easier to implement (in the sense that it might be
reasonably easy to add code to the handling of `pred` so it treats
negation efficiently).

> Another question i have concerns ‘(t . ...)’.  My understanding

This is used for leaves whose *value* is itself a `cons`.
E.g. compare

    (radix-tree-insert nil "ab" '42)
and
    (radix-tree-insert nil "ab" '(42))

So indeed the `t` is used because it's not a string so it shouldn't
match any normal code's `car`.


        Stefan




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

end of thread, other threads:[~2018-06-04 14:43 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2018-05-30 12:28 EXPVAL in pcase-defmacro docstrings Stefan Monnier
2018-05-31 14:56 ` Thien-Thi Nguyen
2018-06-04 14:43   ` Stefan Monnier

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.