unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Question on pcase
@ 2015-10-22 14:46 Oleh Krehel
  2015-10-22 21:19 ` Michael Heerdegen
  0 siblings, 1 reply; 51+ messages in thread
From: Oleh Krehel @ 2015-10-22 14:46 UTC (permalink / raw)
  To: emacs-devel; +Cc: monnier


To be frank, I'm not a fan of `pcase', and really prefer a "for dummies"
coding style. But if it makes other people feel more productive, I'm
fine with it as long as I can read and debug it.

One piece that I'm missing currently is the ability to eval a `pcase'
pattern.  To explain what I mean, here's a function that I was debugging
just now:

(defun completion-at-point ()
  "Perform completion on the text around point.
The completion method is determined by `completion-at-point-functions'."
  (interactive)
  (let ((res (run-hook-wrapped 'completion-at-point-functions
                               #'completion--capf-wrapper 'all)))
    (pcase res
      (`(,_ . ,(and (pred functionp) f)) (funcall f))
      (`(,hookfun . (,start ,end ,collection . ,plist))
       (unless (markerp start) (setq start (copy-marker start)))
       (let* ((completion-extra-properties plist)
              (completion-in-region-mode-predicate
               (lambda ()
                 ;; We're still in the same completion field.
                 (let ((newstart (car-safe (funcall hookfun))))
                   (and newstart (= newstart start))))))
         (completion-in-region start end collection
                               (plist-get plist :predicate))))
      ;; Maybe completion already happened and the function returned t.
      (_
       (when (cdr res)
         (message "Warning: %S failed to return valid completion data!"
                  (car res)))
       (cdr res)))))

I have `res' stored in a global variable and would like to know which
branch will match, and store e.g. `hookfn' etc in global variables as
well.

What I want is an interface that allows me to position the point like
this (just so it's clear which branch I've selected):

    |(`(,hookfun . (,start ,end ,collection . ,plist))
    
and press something akin to "C-x C-e" that either says "The pattern did
not match", or evaluates:

(progn
  (setq hook-fn (car res))
  (setq rest (cdr res))
  (setq start (nth 0 (cdr res)))
  (setq end (nth 1 (cdr res)))
  (setq collection (nth 2 (cdr res)))
  (setq plist (nthcdr 3 (cdr res))))

The single function that I need is basically:

(should (equal
         (pcase--expand-if-match
          '(,hookfun . (,start ,end ,collection . ,plist))
          'res)
         '(progn
           (setq hookfun (car res))
           (setq start (nth 0 (cdr res)))
           (setq end (nth 1 (cdr res)))
           (setq collection (nth 2 (cdr res)))
           (setq plist (nthcdr 3 (cdr res))))))

Could someone please help me to implement `pcase--expand-if-match'. I
think it should be trivial to do for a person that understands how
`pcase' works. I don't want to learn how to write `pcase', but the above
functionality would be more than enough for me to be able to read and
edit its inner contents.

thanks in advance,
Oleh



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

* Re: Question on pcase
  2015-10-22 14:46 Question on pcase Oleh Krehel
@ 2015-10-22 21:19 ` Michael Heerdegen
  2015-10-23  6:30   ` Eli Zaretskii
  0 siblings, 1 reply; 51+ messages in thread
From: Michael Heerdegen @ 2015-10-22 21:19 UTC (permalink / raw)
  To: emacs-devel

Hello Oleh,

> To be frank, I'm not a fan of `pcase', and really prefer a "for dummies"
> coding style. But if it makes other people feel more productive, I'm
> fine with it as long as I can read and debug it.
>
> One piece that I'm missing currently is the ability to eval a `pcase'
> pattern.

Did you try to `macroexpand' a pcase form?  The result is readable,
though a bit long.

Note that `pcase' doesn't set variables, it is a binding construct.

Since the "matching" part and the "binding" part aren't separate, but
binding happens as a side effect while matching, your approach to
understand the thing seems not good to me.

IME the best way to transform pcase expressions into a readable form is
to learn to read them.  I can only advertise to give it a try.  A good
starting point is

   (info "(elisp) Pattern matching case statement")

(can be improved...)

But I totally understand if you don't want to learn it.  FWIW I never
understood bash syntax, and also don't want to learn it :-P


Regards,

Michael. 




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

* Re: Question on pcase
  2015-10-22 21:19 ` Michael Heerdegen
@ 2015-10-23  6:30   ` Eli Zaretskii
  2015-10-23 11:58     ` Oleh Krehel
  2015-10-23 12:17     ` Michael Heerdegen
  0 siblings, 2 replies; 51+ messages in thread
From: Eli Zaretskii @ 2015-10-23  6:30 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: emacs-devel

> From: Michael Heerdegen <michael_heerdegen@web.de>
> Date: Thu, 22 Oct 2015 23:19:18 +0200
> 
> > To be frank, I'm not a fan of `pcase', and really prefer a "for dummies"
> > coding style. But if it makes other people feel more productive, I'm
> > fine with it as long as I can read and debug it.
> >
> > One piece that I'm missing currently is the ability to eval a `pcase'
> > pattern.
> 
> Did you try to `macroexpand' a pcase form?  The result is readable,
> though a bit long.
> 
> Note that `pcase' doesn't set variables, it is a binding construct.
> 
> Since the "matching" part and the "binding" part aren't separate, but
> binding happens as a side effect while matching, your approach to
> understand the thing seems not good to me.
> 
> IME the best way to transform pcase expressions into a readable form is
> to learn to read them.  I can only advertise to give it a try.  A good
> starting point is
> 
>    (info "(elisp) Pattern matching case statement")
> 
> (can be improved...)
> 
> But I totally understand if you don't want to learn it.  FWIW I never
> understood bash syntax, and also don't want to learn it :-P

Maybe we should have some higher-level facility to expand a pcase into
a human-readable form.  Would something like this be possible and
useful?



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

* Re: Question on pcase
  2015-10-23  6:30   ` Eli Zaretskii
@ 2015-10-23 11:58     ` Oleh Krehel
  2015-10-23 12:23       ` Michael Heerdegen
  2015-10-23 12:17     ` Michael Heerdegen
  1 sibling, 1 reply; 51+ messages in thread
From: Oleh Krehel @ 2015-10-23 11:58 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: Michael Heerdegen, emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

> Maybe we should have some higher-level facility to expand a pcase into
> a human-readable form.  Would something like this be possible and
> useful?

Yes, this is almost exactly what I mean. And there's some functionality
for this in Edebug. The thing is that I don't want to go through Edebug
and want to just step though things with `eval-last-sexp'.

The major disadvantage currently is that I can't "step into" a `pcase'
with "C-x C-e": I can either evaluate the whole thing or nothing.  I
really want to be able to evaluate a single branch.  I'd be happy to add
this functionality, I just need a little guidance so that I don't
reinvent the wheel.



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

* Re: Question on pcase
  2015-10-23  6:30   ` Eli Zaretskii
  2015-10-23 11:58     ` Oleh Krehel
@ 2015-10-23 12:17     ` Michael Heerdegen
  2015-10-23 12:22       ` Oleh Krehel
  2015-10-23 13:26       ` Eli Zaretskii
  1 sibling, 2 replies; 51+ messages in thread
From: Michael Heerdegen @ 2015-10-23 12:17 UTC (permalink / raw)
  To: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

> Maybe we should have some higher-level facility to expand a pcase into
> a human-readable form.  Would something like this be possible and
> useful?

Possible - maybe.  Useful: IMHO no.  `pcase' is simple and easy to
learn.  It just looks a bit frightening and strange at first.  I would
rather improve documentation or write an easy intro than invite people
to avoid learning an excellent tool.


Michael.




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

* Re: Question on pcase
  2015-10-23 12:17     ` Michael Heerdegen
@ 2015-10-23 12:22       ` Oleh Krehel
  2015-10-23 13:26       ` Eli Zaretskii
  1 sibling, 0 replies; 51+ messages in thread
From: Oleh Krehel @ 2015-10-23 12:22 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: emacs-devel

Michael Heerdegen <michael_heerdegen@web.de> writes:

> Eli Zaretskii <eliz@gnu.org> writes:
>
>> Maybe we should have some higher-level facility to expand a pcase into
>> a human-readable form.  Would something like this be possible and
>> useful?
>
> Possible - maybe.  Useful: IMHO no.  `pcase' is simple and easy to
> learn.  It just looks a bit frightening and strange at first.  I would
> rather improve documentation or write an easy intro than invite people
> to avoid learning an excellent tool.

I think it would be very useful. Take, for instance, `cond' - it's a
perfect abstraction and allows you to step into any branch that you
like. Whether it's easy to understand or not, I'd like `pcase' to offer
the same option.

I'm sure that even if I knew all the intricacies of `pcase', I'd still
want an option to "step-in", just like I enjoy this option for `cond`
right now. It's a matter of good tooling, not good documentation.




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

* Re: Question on pcase
  2015-10-23 11:58     ` Oleh Krehel
@ 2015-10-23 12:23       ` Michael Heerdegen
  2015-10-23 12:42         ` Oleh Krehel
  0 siblings, 1 reply; 51+ messages in thread
From: Michael Heerdegen @ 2015-10-23 12:23 UTC (permalink / raw)
  To: emacs-devel

Hi Oleh,

I guess you might like something like this:

--8<---------------cut here---------------start------------->8---
(defun pcase-demystify-pattern (pattern)
  (pcase--u
   `((,(pcase--match '-tested-expression- (pcase--macroexpand pattern))
      ,(lambda (vars) `(progn ,@(mapcar (lambda (b) `(-bind- ,(car b) ,(cdr b)))
                                   vars)))))))
--8<---------------cut here---------------end--------------->8---

You need to quote the pattern because this is a function.

(pcase-demystify-pattern '`(,_ . ,(and (pred functionp) f)))

  ==>

    (if
        (consp -tested-expression-)
        (let*
            ((#1=#:x
              (cdr -tested-expression-)))
          (if
              (functionp #1#)
              (progn
                (-bind- f #1#))
            nil))
      nil)


Michael.




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

* Re: Question on pcase
  2015-10-23 12:23       ` Michael Heerdegen
@ 2015-10-23 12:42         ` Oleh Krehel
  2015-10-23 13:07           ` Michael Heerdegen
  0 siblings, 1 reply; 51+ messages in thread
From: Oleh Krehel @ 2015-10-23 12:42 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: emacs-devel

Michael Heerdegen <michael_heerdegen@web.de> writes:

> I guess you might like something like this:
>
> (defun pcase-demystify-pattern (pattern)
>   (pcase--u
>    `((,(pcase--match '-tested-expression- (pcase--macroexpand pattern))
>       ,(lambda (vars) `(progn ,@(mapcar (lambda (b) `(-bind- ,(car b) ,(cdr b)))
>                                    vars)))))))
>
> You need to quote the pattern because this is a function.
>
> (pcase-demystify-pattern '`(,_ . ,(and (pred functionp) f)))
>
>   ==>
>
>     (if
>         (consp -tested-expression-)
>         (let*
>             ((#1=#:x
>               (cdr -tested-expression-)))
>           (if
>               (functionp #1#)
>               (progn
>                 (-bind- f #1#))
>             nil))
>       nil)

Thanks Michael, that's almost exactly what I need. The only thing is
that I don't understand what `-bind-' is.



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

* Re: Question on pcase
  2015-10-23 12:42         ` Oleh Krehel
@ 2015-10-23 13:07           ` Michael Heerdegen
  2015-10-23 13:26             ` Oleh Krehel
  0 siblings, 1 reply; 51+ messages in thread
From: Michael Heerdegen @ 2015-10-23 13:07 UTC (permalink / raw)
  To: Oleh Krehel; +Cc: emacs-devel

Oleh Krehel <ohwoeowho@gmail.com> writes:

> Thanks Michael, that's almost exactly what I need. The only thing is
> that I don't understand what `-bind-' is.

That's just a placeholder I used.  Depending on what pcase form you are
using (`pcase', `pcase-lambda', `pcase-let'), the semantic is a bit
different.

You can just imagine that it creates a local binding, like `let', and
that binding is available in the CODE part of the

  (PATTERN CODE...)

of this branch.

The actual code `pcase' produces is easy as well but longer; when I
included this aspect in `pcase-demystify-pattern', the result would look
more like what you would get with `macroexpand', so I don't think it
would be useful, since the semantic is also slightly different in other
`pcase' derived macros.


Michael.



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

* Re: Question on pcase
  2015-10-23 13:07           ` Michael Heerdegen
@ 2015-10-23 13:26             ` Oleh Krehel
  2015-10-23 13:59               ` Michael Heerdegen
  2015-10-23 14:23               ` Michael Heerdegen
  0 siblings, 2 replies; 51+ messages in thread
From: Oleh Krehel @ 2015-10-23 13:26 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: emacs-devel

Michael Heerdegen <michael_heerdegen@web.de> writes:

> Oleh Krehel <ohwoeowho@gmail.com> writes:
>
>> Thanks Michael, that's almost exactly what I need. The only thing is
>> that I don't understand what `-bind-' is.
>
> That's just a placeholder I used.  Depending on what pcase form you are
> using (`pcase', `pcase-lambda', `pcase-let'), the semantic is a bit
> different.
>
> You can just imagine that it creates a local binding, like `let', and
> that binding is available in the CODE part of the
>
>   (PATTERN CODE...)
>
> of this branch.
>
> The actual code `pcase' produces is easy as well but longer; when I
> included this aspect in `pcase-demystify-pattern', the result would look
> more like what you would get with `macroexpand', so I don't think it
> would be useful, since the semantic is also slightly different in other
> `pcase' derived macros.

I think there's a bit of miscommunication here. I can `macroexpand' as
well, but the branch info isn't clear-cut in that case.  The thing is
that I don't want to use `pcase-demystify-pattern' to understand
approximately what each pattern does, I want it to expand to the exact
code that is run for a branch, and I want to run that code.

This can all be done in-place without producing extra buffers. A simple
"C-c C-e" on a branch should either:

    (message "the branch doesn't match")

or e.g.:

    (progn
      (setq hookfun (car res))
      (setq start (nth 0 (cdr res)))
      (setq end (nth 1 (cdr res)))
      (setq collection (nth 2 (cdr res)))
      (setq plist (nthcdr 3 (cdr res)))
      (message "match"))

Instead, (pcase-demystify-pattern '`(,hookfun . (,start ,end ,collection . ,plist)))
gives:

    (if (consp -tested-expression-)
        (let* ((x (car -tested-expression-))
               (x (cdr -tested-expression-)))
          (if (consp x)
              (let* ((x (car x))
                     (x (cdr x)))
                (if (consp x)
                    (let* ((x (car x))
                           (x (cdr x)))
                      (if (consp x)
                          (let* ((x (car x))
                                 (x (cdr x)))
                            (progn
                              (-bind- plist x)
                              (-bind- collection x)
                              (-bind- end x)
                              (-bind- start x)
                              (-bind- hookfun x)))
                        nil))
                  nil))
            nil))
      nil)

Which is indeed close to what I need. I guess `-bind-` should be some
macro that pops and does a `set'. I'm just not sure that this is exactly
true, and will work for all patterns.



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

* Re: Question on pcase
  2015-10-23 12:17     ` Michael Heerdegen
  2015-10-23 12:22       ` Oleh Krehel
@ 2015-10-23 13:26       ` Eli Zaretskii
  2015-10-23 14:14         ` Michael Heerdegen
  1 sibling, 1 reply; 51+ messages in thread
From: Eli Zaretskii @ 2015-10-23 13:26 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: emacs-devel

> From: Michael Heerdegen <michael_heerdegen@web.de>
> Date: Fri, 23 Oct 2015 14:17:35 +0200
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> > Maybe we should have some higher-level facility to expand a pcase into
> > a human-readable form.  Would something like this be possible and
> > useful?
> 
> Possible - maybe.  Useful: IMHO no.  `pcase' is simple and easy to
> learn.

Evidently, at least one developer found it daunting.

> It just looks a bit frightening and strange at first.  I would
> rather improve documentation or write an easy intro than invite
> people to avoid learning an excellent tool.

How about doing both?  Maybe that will prompt people to learn about
pcase rather than hate it and stay away of it?



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

* Re: Question on pcase
  2015-10-23 13:26             ` Oleh Krehel
@ 2015-10-23 13:59               ` Michael Heerdegen
  2015-10-27 23:50                 ` Johan Bockgård
  2015-10-23 14:23               ` Michael Heerdegen
  1 sibling, 1 reply; 51+ messages in thread
From: Michael Heerdegen @ 2015-10-23 13:59 UTC (permalink / raw)
  To: Oleh Krehel; +Cc: emacs-devel

Oleh Krehel <ohwoeowho@gmail.com> writes:

> This can all be done in-place without producing extra buffers. A simple
> "C-c C-e" on a branch should either:
>
>     (message "the branch doesn't match")
>
> or e.g.:
>
>     (progn
>       (setq hookfun (car res))
>       (setq start (nth 0 (cdr res)))
>       (setq end (nth 1 (cdr res)))
>       (setq collection (nth 2 (cdr res)))
>       (setq plist (nthcdr 3 (cdr res)))
>       (message "match"))

That doesn't make sense to me.  In real-life code, you have free
variables, and whether a branch matches depends on the current
environment.  E.g. if you have

(pcase x
 (1 t)
 (_ nil))

and you put the cursor after the "1" pattern and hit C-c C-e, what would
you expect to happen?  Whether that branch matches depends on the
current binding of x.

> Instead, (pcase-demystify-pattern '`(,hookfun . (,start ,end
> ,collection . ,plist))) gives:
>
>     (if (consp -tested-expression-)
>         (let* ((x (car -tested-expression-))
>                (x (cdr -tested-expression-)))
>           (if (consp x)
>               (let* ((x (car x))
>                      (x (cdr x)))
>                 (if (consp x)
>                     (let* ((x (car x))
>                            (x (cdr x)))
>                       (if (consp x)
>                           (let* ((x (car x))
>                                  (x (cdr x)))
>                             (progn
>                               (-bind- plist x)
>                               (-bind- collection x)
>                               (-bind- end x)
>                               (-bind- start x)
>                               (-bind- hookfun x)))
>                         nil))
>                   nil))
>             nil))
>       nil)

The Lisp printer discarded relevant information here, please do

(setq print-gensym t)
(setq print-circle t)

when printing a result of `pcase-demystify-pattern'.  After you have
done that, you exactly see how matching is done.

> Which is indeed close to what I need. I guess `-bind-` should be some
> macro that pops and does a `set'. I'm just not sure that this is exactly
> true, and will work for all patterns.

The only thing that makes sense to me would be to make `edebug' let you
step into a pattern.  AFAICT that would be a very hard work.


Michael.



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

* Re: Question on pcase
  2015-10-23 13:26       ` Eli Zaretskii
@ 2015-10-23 14:14         ` Michael Heerdegen
  2015-10-23 14:41           ` Eli Zaretskii
  0 siblings, 1 reply; 51+ messages in thread
From: Michael Heerdegen @ 2015-10-23 14:14 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

> > Possible - maybe.  Useful: IMHO no.  `pcase' is simple and easy to
> > learn.
>
> Evidently, at least one developer found it daunting.

I found it daunting too before I tried to learn it. 

> > It just looks a bit frightening and strange at first.  I would
> > rather improve documentation or write an easy intro than invite
> > people to avoid learning an excellent tool.
>
> How about doing both?  Maybe that will prompt people to learn about
> pcase rather than hate it and stay away of it?

Sure.  Let me think about what would make sense.  I think we should
first write some simple guide to understand pcase.  If anything more is
needed then, ok, let's see what this is.  For now, the "demystify" thing
is the best thing I can offer for someone who doesn't want to learn it.

The problem here is that the semantic of `pcase' is so many-sided.  You
can use it like `let'.  You can use it like `cond'.  You can use it like
`case'.  You can use it like `destructuring-bind'.  All combined, and
more.  So there obviously must be a quite a bit of stuff to learn and
remember about pcase, and you have to practise it a bit.


Michael.



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

* Re: Question on pcase
  2015-10-23 13:26             ` Oleh Krehel
  2015-10-23 13:59               ` Michael Heerdegen
@ 2015-10-23 14:23               ` Michael Heerdegen
  1 sibling, 0 replies; 51+ messages in thread
From: Michael Heerdegen @ 2015-10-23 14:23 UTC (permalink / raw)
  To: Oleh Krehel; +Cc: emacs-devel

Oleh Krehel <ohwoeowho@gmail.com> writes:

>     (progn
>       (setq hookfun (car res))
>       (setq start (nth 0 (cdr res)))
>       (setq end (nth 1 (cdr res)))
>       (setq collection (nth 2 (cdr res)))
>       (setq plist (nthcdr 3 (cdr res)))
>       (message "match"))

And especially this part also depends on the environment.  What bindings
are created does not only depend on whether a branch matches or not.

By accident, in the

  `(,hookfun . (,start ,end ,collection . ,plist))

example, you can mentally separate those two things, but in general, you
can't.


Michael.



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

* Re: Question on pcase
  2015-10-23 14:14         ` Michael Heerdegen
@ 2015-10-23 14:41           ` Eli Zaretskii
  2015-10-23 18:38             ` Michael Heerdegen
  0 siblings, 1 reply; 51+ messages in thread
From: Eli Zaretskii @ 2015-10-23 14:41 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: emacs-devel

> From: Michael Heerdegen <michael_heerdegen@web.de>
> Cc: emacs-devel@gnu.org
> Date: Fri, 23 Oct 2015 16:14:42 +0200
> 
> I think we should first write some simple guide to understand pcase.
> If anything more is needed then, ok, let's see what this is.  For
> now, the "demystify" thing is the best thing I can offer for someone
> who doesn't want to learn it.

Sounds good to me, thanks.



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

* Re: Question on pcase
  2015-10-23 14:41           ` Eli Zaretskii
@ 2015-10-23 18:38             ` Michael Heerdegen
  2015-10-23 18:43               ` Eli Zaretskii
                                 ` (4 more replies)
  0 siblings, 5 replies; 51+ messages in thread
From: Michael Heerdegen @ 2015-10-23 18:38 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: Oleh Krehel, Emacs Development

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

Eli Zaretskii <eliz@gnu.org> writes:

> Sounds good to me, thanks.

I wrote a step by step introduction for people who (1) are frightened by
`pcase' and (2) are willing to invest some (not much, but some) time to
learn it.  It is how I would describe `pcase' to a friend, maybe it is
good for learning, maybe it is ugly, dunno.  Some errors included.

Someone who wants to try to learn pcase (Oleh? Oleh!) can help by
reading it and telling me if it is understandable, and send corrections
- or write a better introduction ;-) - and format it nicely for
inclusion into Elpa or Emacs when it turns out to help people.

This is an .el only because it was easier for me to format the code
parts this way.



[-- Attachment #2: pcase-guide.el --]
[-- Type: application/emacs-lisp, Size: 11978 bytes --]

[-- Attachment #3: Type: text/plain, Size: 20 bytes --]



Thanks,

Michael.

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

* Re: Question on pcase
  2015-10-23 18:38             ` Michael Heerdegen
@ 2015-10-23 18:43               ` Eli Zaretskii
  2015-10-23 19:59               ` Przemysław Wojnowski
                                 ` (3 subsequent siblings)
  4 siblings, 0 replies; 51+ messages in thread
From: Eli Zaretskii @ 2015-10-23 18:43 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: ohwoeowho, emacs-devel

> From: Michael Heerdegen <michael_heerdegen@web.de>
> Date: Fri, 23 Oct 2015 20:38:31 +0200
> Cc: Oleh Krehel <ohwoeowho@gmail.com>, Emacs Development <emacs-devel@gnu.org>
> 
> I wrote a step by step introduction for people who (1) are frightened by
> `pcase' and (2) are willing to invest some (not much, but some) time to
> learn it.  It is how I would describe `pcase' to a friend, maybe it is
> good for learning, maybe it is ugly, dunno.  Some errors included.

Thanks.

> This is an .el only because it was easier for me to format the code
> parts this way.

You could make it a section in emacs-lisp-intro.texi by making the
comments into (marked-up) text and the code into @example's.



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

* Re: Question on pcase
  2015-10-23 18:38             ` Michael Heerdegen
  2015-10-23 18:43               ` Eli Zaretskii
@ 2015-10-23 19:59               ` Przemysław Wojnowski
  2015-10-23 21:01                 ` Michael Heerdegen
  2015-10-23 20:23               ` Przemysław Wojnowski
                                 ` (2 subsequent siblings)
  4 siblings, 1 reply; 51+ messages in thread
From: Przemysław Wojnowski @ 2015-10-23 19:59 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: Emacs Development

W dniu 23.10.2015 o 20:38, Michael Heerdegen pisze:
> I wrote a step by step introduction for people who (1) are frightened by
> `pcase' and (2) are willing to invest some (not much, but some) time to
> learn it.  It is how I would describe `pcase' to a friend, maybe it is
> good for learning, maybe it is ugly, dunno.  Some errors included.
>
> Someone who wants to try to learn pcase (Oleh? Oleh!) can help by
> reading it and telling me if it is understandable, and send corrections
> - or write a better introduction ;-) - and format it nicely for
> inclusion into Elpa or Emacs when it turns out to help people.

Thanks for the tutorial! :-)
I didn't know what pcase is until I read the first few lines. Then I 
just realized that it's just a version of Pattern Matching that I know 
from Clojure (https://youtu.be/mi3OtBc73-k - is a good intro).

Anyway, I read the tutorial and IMHO it is mostly clear. I just have a 
few questions and comments:

line 103:
;; that's the case only for the string "Hallo" (why would it not be
;; useful to leave out the quote before the symbol `a'?

Is it a question to the reader or you just forgot to remove?
---------------
line 136:
;; If matching any pattern established a variable binding, you can
;; refer to these bindings in the following patterns.

Shouldn't end with "[...]the following patterns in the same branch."? 
Without this there's a little confusion on whether the binding is also 
available in the below branches too.
---------------
Around line 177 - (let PAT EXP) example:
(pcase x
   ((and (pred numberp)
         (let (pred (lambda (x) (< 5 x))) (abs x)))
    t)
   (_ nil))
IMHO it would be better to simplify this example to contain only "let" part:
(pcase x
   ((let (pred (lambda (x) (< 5 x))) (abs x))
    t)
   (_ nil))
And in explanation write order of evaluation: result of EXP(x) is passed 
to PAT. Anyway, this part wasn't clear to me.
---------------
What are some good (concrete) use cases for the pcase? When would you 
suggest to use it instead of cond or other similar constructs?
---------------

Great job!

Thanks,
Przemysław



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

* Re: Question on pcase
  2015-10-23 18:38             ` Michael Heerdegen
  2015-10-23 18:43               ` Eli Zaretskii
  2015-10-23 19:59               ` Przemysław Wojnowski
@ 2015-10-23 20:23               ` Przemysław Wojnowski
  2015-10-23 20:39                 ` Michael Heerdegen
  2015-10-24  9:01               ` Alan Mackenzie
  2015-10-26 15:55               ` Oleh Krehel
  4 siblings, 1 reply; 51+ messages in thread
From: Przemysław Wojnowski @ 2015-10-23 20:23 UTC (permalink / raw)
  To: Michael Heerdegen, Eli Zaretskii; +Cc: Oleh Krehel, Emacs Development

I don't understand one thing. If "pcase" in an implementation of
Pattern Matching, then why it is called "pcase" not "pmatch"?

"pmatch" would even match better. ;-)



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

* Re: Question on pcase
  2015-10-23 20:23               ` Przemysław Wojnowski
@ 2015-10-23 20:39                 ` Michael Heerdegen
  2015-10-24 11:37                   ` Przemysław Wojnowski
  0 siblings, 1 reply; 51+ messages in thread
From: Michael Heerdegen @ 2015-10-23 20:39 UTC (permalink / raw)
  To: Przemysław Wojnowski; +Cc: Eli Zaretskii, Oleh Krehel, Emacs Development

Przemysław Wojnowski <esperanto@cumego.com> writes:

> I don't understand one thing. If "pcase" in an implementation of
> Pattern Matching, then why it is called "pcase" not "pmatch"?

`pcase' itself is just one macro that pcase.el defines that makes use of
pattern matching.  I guess the name means Pattern (matching) Case,
because it's syntax and semantic is similar to `cl-case', just with
pattern matching.


Michael.



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

* Re: Question on pcase
  2015-10-23 19:59               ` Przemysław Wojnowski
@ 2015-10-23 21:01                 ` Michael Heerdegen
  0 siblings, 0 replies; 51+ messages in thread
From: Michael Heerdegen @ 2015-10-23 21:01 UTC (permalink / raw)
  To: Przemysław Wojnowski; +Cc: Emacs Development

Przemysław Wojnowski <esperanto@cumego.com> writes:

> line 103:
> ;; that's the case only for the string "Hallo" (why would it not be
> ;; useful to leave out the quote before the symbol `a'?
>
> Is it a question to the reader or you just forgot to remove?

A question to the reader - forgetting to quote a symbol is a common
pitfall.  If the user works the reason out for herself, it is probably
easier to remember.

> ---------------
> line 136:
> ;; If matching any pattern established a variable binding, you can
> ;; refer to these bindings in the following patterns.
>
> Shouldn't end with "[...]the following patterns in the same branch."?
> Without this there's a little confusion on whether the binding is also
> available in the below branches too.

Good catch, indeed.

> ---------------
> Around line 177 - (let PAT EXP) example:
> (pcase x
>   ((and (pred numberp)
>         (let (pred (lambda (x) (< 5 x))) (abs x)))
>    t)
>   (_ nil))
> IMHO it would be better to simplify this example to contain only "let" part:
> (pcase x
>   ((let (pred (lambda (x) (< 5 x))) (abs x))
>    t)
>   (_ nil))
> And in explanation write order of evaluation: result of EXP(x) is
> passed to PAT. Anyway, this part wasn't clear to me.

Mmh, so this part is a bit unclear.  Thanks, maybe I can even find a
better suited example.


> What are some good (concrete) use cases for the pcase? When would you
> suggest to use it instead of cond or other similar constructs?

IME you know when you need it after having learned it.

The first times where you use it in your code will probably be cases
where a `case' or a `cond' nearly fit, but is not sufficient.
E.g. where a `cl-case' would be appropriate for most branches but you
need to test a condition in one branch.

Start like that, and keep in mind that `pcase' is in your toolbox while
coding.

For concrete use cases - just see the Emacs sources!  But it's hard to
describe the typical use case for `pcase' as it is hard to describe a
typical use case for a tool chest.


Thanks for your feedback!

Michael.



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

* Re: Question on pcase
  2015-10-23 18:38             ` Michael Heerdegen
                                 ` (2 preceding siblings ...)
  2015-10-23 20:23               ` Przemysław Wojnowski
@ 2015-10-24  9:01               ` Alan Mackenzie
  2015-10-24 12:58                 ` Stephen Berman
  2015-10-24 17:00                 ` Question on pcase Drew Adams
  2015-10-26 15:55               ` Oleh Krehel
  4 siblings, 2 replies; 51+ messages in thread
From: Alan Mackenzie @ 2015-10-24  9:01 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: Eli Zaretskii, Oleh Krehel, Emacs Development

Hello, Michael.

On Fri, Oct 23, 2015 at 08:38:31PM +0200, Michael Heerdegen wrote:
> Eli Zaretskii <eliz@gnu.org> writes:

> > Sounds good to me, thanks.

> I wrote a step by step introduction for people who (1) are frightened by
> `pcase' and (2) are willing to invest some (not much, but some) time to
> learn it.  It is how I would describe `pcase' to a friend, maybe it is
> good for learning, maybe it is ugly, dunno.  Some errors included.

> Someone who wants to try to learn pcase (Oleh? Oleh!) can help by
> reading it and telling me if it is understandable, and send corrections
> - or write a better introduction ;-) - and format it nicely for
> inclusion into Elpa or Emacs when it turns out to help people.

Thanks for writing this.  I've enclosed some corrections - a few typos,
and some places where the English usage isn't quite correct.

Some notes:
1. The guide you have written is _not_ trivial.  :-)  It is detailed and
helpful.

2. "Semantics", as a noun, is always plural in English (a bit like
"Eltern" in German).  It is also a mass noun, so you couldn't write "one
of the semantics is ...."; you'd need to write something like "one part
of the semantics is ....".

3. "Else" can't come at the end of a sentence.  (It can't really come at
the start of a sentence either.)  If you're going to use "else" it needs
to come _before_ the clause it applies to.

4. In "How would you roughly describe ...", the "roughly" means doing the
describing without delicacy, perhaps with violence, without any finesse.
In "How, roughly, would you describe ...", it means "approximately".
:-)

5. "the `pred' pattern form allows to simplify ...." is a very common
solecism.  The verb "allow" needs a direct object, not an infinitive verb.
That object can either be the person (or thing) who is being allowed to do
something, or the thing (a noun or gerund) which is being allowed.  So
here, you'd want one of
  o - "the `pred' pattern form allows YOU to simplify ...." or
  o - "the `pred' pattern form allows THE SIMPLIFICATION OF ...." or even 
  o - "the `pred' pattern form allows SIMPLIFYING ...."
I think the first of these is marginally the best.

6. "Now, only one basic pattern form is missing:".  Well, it's not
missing, it's there.  There's no snappy translation of "es fehlt", in
this sense.  You'd have to qualify it with something like "Now, only one
basic pattern form is missing from our list so far:", which is a bit
clumsy.  What I've suggested in my patch is, perhaps, a little less
clumsy.

7. "If you are used to understand grammers ...".  "Used to" takes a
direct object (or gerund), not an infinitive verb.  "Grammar" doesn't
have an "e" in it.  :-)


Here's the patch (not guaranteed complete).



--- pcase-guide.el~	2015-10-24 07:16:29.000000000 +0000
+++ pcase-guide.el	2015-10-24 08:52:53.000000000 +0000
@@ -1,4 +1,4 @@
-;; Understand `pcase' now! - A trivial guide to a powerful tool
+;; Understand `pcase' now! - An easy guide to a powerful tool
 ;; ============================================================
 
 
@@ -23,24 +23,24 @@
 ;; specify PATTERNs instead of CONDITIONs, and the PATTERNS are tested
 ;; for whether they match the evaluated EXPRESSION, in order.  If one
 ;; PATTERN matches, it's CODE is evaluated.  Like in `cond', all
-;; branches are tested until one is found whose PATTERN matches.  All
+;; branches are tested until one whose PATTERN matches is found.  All
 ;; remaining branches are ignored.
 ;;
 ;; The PATTERNs are not lisp code, like `cl-loop' forms they are
-;; written with an own simple language.  There are a few simple basic
+;; written in their own simple language.  There are a few simple basic
 ;; patterns, and you can combine them.
 ;;
 ;; A very useful thing is that matching a pattern can bind variables
-;; as a side effect.  This bindings are in effect when the CODE of
+;; as a side effect.  These bindings are in effect when the CODE of
 ;; that branch is executed (actually, they are already in effect when
-;; then the remaining parts of the pattern are processed; that fact
+;; the remaining parts of the pattern are processed; that fact
 ;; will prove useful later in this guide).
 ;;
-;; All yet to understand is the semantic each of the basic PATTERNs.
+;; All that remains to understand are the semantics of each of the basic PATTERNs.
 ;; Since you can do quite a lot with `pcase' - e.g. you can use it
 ;; like `let*', like `cond', like `case', like `destructuring-bind',
-;; all in combination and more, it is clear that the semantic can't be
-;; absolutely trivial.  However, there are only very few basic forms
+;; all in combination and more, it is clear that the semantics can't be
+;; absolutely trivial.  However, there are only a very few basic forms
 ;; of patterns, and all are easy to understand.  And those forms can
 ;; be combined.  That's already it.  I will explain the basic patterns
 ;; one after the other, you may want to take one or two breaks in
@@ -80,7 +80,7 @@
   (_        3))
 
 ;; This will return 1 if x is bound to the symbol `a', 2 if x is bound
-;; to a string equal to "Hallo", and 3 else.  Hey, already two pattern
+;; to a string equal to "Hallo", and 3 otherwise.  Hey, already two pattern
 ;; types learned!
 ;;
 ;; A SYMBOL (different from `_') "matches anything and binds it to
@@ -129,7 +129,7 @@
                                       3))
 
 ;; This will return 1 if x is bound to a list, 2 if what x is bound to
-;; fulfils eihter `arrayp' or `numberp'.  And 3 if the binding of x is
+;; fulfils either `arrayp' or `numberp'.  And 3 if the binding of x is
 ;; a string that contains "blue".  If none of that is true, no branch
 ;; matches, and `pcase' returns nil.
 
@@ -155,7 +155,7 @@
 ;; strange special case of matching via the `guard' pattern.
 
 ;; Exercise: We noted that `pcase' can be used to substitute `let*',
-;; `cond' or `case' in your code.  How would you roughly describe how
+;; `cond' or `case' in your code.  How, roughly, would you describe how
 ;; one can rewrite an arbitrary `let*', `cond' or `case' form using
 ;; `pcase'? (Hint: It's not hard.)
 
@@ -166,7 +166,7 @@
 ;;
 ;; This is a pattern form that allows you to match a pattern PAT
 ;; against an _arbitrary_ expression EXP.  This is not special,
-;; matching PAT is done as you have learned, just against the EXP you
+;; matching PAT is done as you have learned, just on the EXP you
 ;; specify there, and not the EXPRESSION given to pcase at top level.
 ;;
 ;; Example:
@@ -178,7 +178,7 @@
   (_ nil))
 
 ;; This will return t if x is bound to a number whose absolute value
-;; is larger than 5, like 7 or -13, and nil else, e.g. for 0 or -2 or
+;; is larger than 5, like 7 or -13, and nil otherwise, e.g. for 0 or -2 or
 ;; "Hallo".
 
 ;; Why is it called `let' then?  Because you can use it to
@@ -192,9 +192,9 @@
   l)
 
 ;; will return the string length of STRING if it is bound to a string
-;; of non-zero length, and nil else.
+;; of non-zero length, and nil otherwise.
 
-;; BTW, the `pred' pattern form allows to simplify things like
+;; BTW, the `pred' pattern form allows one to simplify things like
 
 (pred (lambda (x) (< 5 x)))
 
@@ -226,7 +226,7 @@
    abs-val))
 
 ;; will return the absolute value of x if x is a number and its
-;; absolute value is larger than 5, and nil else.
+;; absolute value is larger than 5, and nil otherwise.
 
 ;; (app FUN PAT) matches if FUN applied to the object matches PAT.
 
@@ -240,7 +240,7 @@
 ;; will return t if and only if x is bound to a string with length
 ;; smaller than 60 chars.
 
-;; Now, only one basic pattern form is missing:
+;; Now, only one basic pattern form remains to be described:
 ;;
 ;;   `QPAT         Backquote-style pcase patterns.
 ;;
@@ -263,7 +263,7 @@
 
   (1 2 3)  (1 . (2 3))
 
-;; desribe the same list structure using two different reader
+;; describe the same list structure using two different reader
 ;; syntaxes.  When being read by the Lisp reader, those forms are
 ;; equal.  So, in the above description, the
   
@@ -272,14 +272,14 @@
 ;; form can be used to match any kind of arbitrary list, and you don't
 ;; need to use the read syntax using dot.
 ;;
-;; If you are used to understand grammers: the above description of
-;; `QPAT describes a quite simpel grammer.  You make like to try it
+;; If you are used to understanding grammars: the above description of
+;; `QPAT describes a quite simple grammar.  You might like to try it
 ;; out with some examples yourself, it's simple, though the results
-;; might look unfamiliar.  It doesn't take long to familiarize with
+;; might look unfamiliar.  It doesn't take long to familiarize yourself with
 ;; it, though.
 ;;
 ;; Note that though the backquote _syntax_ is used here, the backquote
-;; macro is never called (!), just it's syntax is used to describe
+;; macro is never called (!), just its syntax is used to describe
 ;; patterns for destructuring because it is convenient to write, and
 ;; also a bit related to how backquote works.
 
@@ -297,17 +297,17 @@
 ;; to "foo", it returns the second element.
 ;;
 ;; If x is bound to a cons whose car equals "foo", and the cdr is
-;; listp, this cdr is returned.  Else, nil is returned.
+;; listp, this cdr is returned.  Otherwise, nil is returned.
 ;;
 ;; For destructuring, the `_' pattern is handy to match anything.
 ;; Pitfall: Be sure to unquote `_' if you don't want to match the
 ;; symbol `_' literally, because _ is not a QPAT.
 ;;
-;; Example: The ;; pattern
+;; Example: The pattern
 
 `(1 ,_ . ,_)
 
-;; matches any list whoose first element is 1, and which has at least
+;; matches any list whose first element is 1, and which has at least
 ;; one more element.
 
 ;; Just like with the "real" backquote, you can use "unquote" in lower
@@ -321,7 +321,7 @@
 ;; element is 1, and the second argument is a list of two elements;
 ;; the first element is bound to the variable `x', and the second
 ;; element is bound to `y', and it is tested whether x + y is smaller
-;; than 10.  Only then the whole pattern matches.
+;; than 10.  Only then does the whole pattern match.
 
 ;; Example:
 

> Thanks,

> Michael.

-- 
Alan Mackenzie (Nuremberg, Germany).



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

* Re: Question on pcase
  2015-10-23 20:39                 ` Michael Heerdegen
@ 2015-10-24 11:37                   ` Przemysław Wojnowski
  0 siblings, 0 replies; 51+ messages in thread
From: Przemysław Wojnowski @ 2015-10-24 11:37 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: Emacs Development

W dniu 23.10.2015 o 22:39, Michael Heerdegen pisze:
> Przemysław Wojnowski <esperanto@cumego.com> writes:
>> I don't understand one thing. If "pcase" in an implementation of
>> Pattern Matching, then why it is called "pcase" not "pmatch"?
>
> `pcase' itself is just one macro that pcase.el defines that makes use of
> pattern matching.  I guess the name means Pattern (matching) Case,
> because it's syntax and semantic is similar to `cl-case', just with
> pattern matching.

IMHO it makes sense to rename it to match its concept and hence making it
easier to learn (discover) for people that know the concept from other
languages:
- Clojure: core.match (https://github.com/clojure/core.match)
- Scala: match keyword 
(http://docs.scala-lang.org/tutorials/tour/pattern-matching.html)
- Racket: match form (http://docs.racket-lang.org/guide/match.html)
- F#: match expression (https://msdn.microsoft.com/en-us/library/dd547125.aspx)



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

* Re: Question on pcase
  2015-10-24  9:01               ` Alan Mackenzie
@ 2015-10-24 12:58                 ` Stephen Berman
  2015-10-24 17:47                   ` Alan Mackenzie
  2015-10-24 17:00                 ` Question on pcase Drew Adams
  1 sibling, 1 reply; 51+ messages in thread
From: Stephen Berman @ 2015-10-24 12:58 UTC (permalink / raw)
  To: Alan Mackenzie
  Cc: Michael Heerdegen, Eli Zaretskii, Oleh Krehel, Emacs Development

On Sat, 24 Oct 2015 09:01:46 +0000 Alan Mackenzie <acm@muc.de> wrote:

> 2. "Semantics", as a noun, is always plural in English (a bit like
> "Eltern" in German).

It's morphologically plural but syntactically singular, like "physics"
-- at least in American English, but I think also in British English; or
would you really say "Semantics are the study of meaning" or "Semantics
are a science, and physics are, too"?  Hence, I think in

> -;; All yet to understand is the semantic each of the basic PATTERNs.
> +;; All that remains to understand are the semantics of each of the basic PATTERNs.
                                     ^^^
"are" should be replaced by "is".  (Even if "are" is acceptable here in
British English, AFAIK the standard for GNU documentation is American
English.)

> -;; fulfils eihter `arrayp' or `numberp'.  And 3 if the binding of x is
> +;; fulfils either `arrayp' or `numberp'.  And 3 if the binding of x is
      ^^^^^^^
I believe "fulfills" is the usual spelling in American English.

>  ;; This is a pattern form that allows you to match a pattern PAT
>  ;; against an _arbitrary_ expression EXP.  This is not special,
> -;; matching PAT is done as you have learned, just against the EXP you
> +;; matching PAT is done as you have learned, just on the EXP you
>  ;; specify there, and not the EXPRESSION given to pcase at top level.

Why replace "against" with "on" here but not in the preceding line?  I
think "against" is usual in this context.  A somewhat better formulation
is this, IMO: "The only difference from the pattern matching you have
learned is that PAT is matched against the EXP you specify here..."

> -;; If you are used to understand grammers: the above description of
> -;; `QPAT describes a quite simpel grammer.  You make like to try it
> +;; If you are used to understanding grammars: the above description of
> +;; `QPAT describes a quite simple grammar.  You might like to try it

I think a somewhat better formulation is this: "If you are familiar with
formal language grammars, the above description..."

Steve Berman



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

* RE: Question on pcase
  2015-10-24  9:01               ` Alan Mackenzie
  2015-10-24 12:58                 ` Stephen Berman
@ 2015-10-24 17:00                 ` Drew Adams
  2015-10-24 17:22                   ` Alan Mackenzie
  1 sibling, 1 reply; 51+ messages in thread
From: Drew Adams @ 2015-10-24 17:00 UTC (permalink / raw)
  To: Alan Mackenzie, Michael Heerdegen
  Cc: Eli Zaretskii, Oleh Krehel, Emacs Development

Good comments about the language, Alan.

One suggestion:

> 5. "the `pred' pattern form allows to simplify ...." is a very common
> solecism.  The verb "allow" needs a direct object, not an infinitive verb.
> That object can either be the person (or thing) who is being allowed to do
> something, or the thing (a noun or gerund) which is being allowed.  So
> here, you'd want one of
>   o - "the `pred' pattern form allows YOU to simplify ...." or
>   o - "the `pred' pattern form allows THE SIMPLIFICATION OF ...." or even
>   o - "the `pred' pattern form allows SIMPLIFYING ...."

It's usually simpler and clearer to use "let" than "allows
... to".  But it is usually better still to put the user first,
as the active subject of the action:

  You can use `pred' to ...

or if it's not clear what `pred' is:

  You can use pattern `pred' to ...

or:

  You can simplify ... using `pred'.

The user is the actor; pattern `pred' is the tool.

(I did not look at the original, but a guess might be that
someone reading Info should see PRED here (no quotes), not
`pred'.  Another guess, again without consulting the text,
is that perhaps you don't need both "pattern" and "form":
"pattern form".)

And I too thank Michael for helping us (all) understand the
obtuse creature that is `pcase'.  It is comforting (and all
too rare, it seems) to see such careful interest in helping
Emacs document itself.  Kudos - very helpful.



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

* Re: Question on pcase
  2015-10-24 17:00                 ` Question on pcase Drew Adams
@ 2015-10-24 17:22                   ` Alan Mackenzie
  2015-10-24 17:36                     ` Drew Adams
  2015-10-24 20:03                     ` Johan Bockgård
  0 siblings, 2 replies; 51+ messages in thread
From: Alan Mackenzie @ 2015-10-24 17:22 UTC (permalink / raw)
  To: Drew Adams
  Cc: Michael Heerdegen, Eli Zaretskii, Oleh Krehel, Emacs Development

Hello, Drew

On Sat, Oct 24, 2015 at 10:00:05AM -0700, Drew Adams wrote:
> Good comments about the language, Alan.

Thanks!

> One suggestion:

> > 5. "the `pred' pattern form allows to simplify ...." is a very common
> > solecism.  The verb "allow" needs a direct object, not an infinitive verb.
> > That object can either be the person (or thing) who is being allowed to do
> > something, or the thing (a noun or gerund) which is being allowed.  So
> > here, you'd want one of
> >   o - "the `pred' pattern form allows YOU to simplify ...." or
> >   o - "the `pred' pattern form allows THE SIMPLIFICATION OF ...." or even
> >   o - "the `pred' pattern form allows SIMPLIFYING ...."

> It's usually simpler and clearer to use "let" than "allows
> ... to".

Linguistically, "let" is as complicated as "allow".  It's also a bit
more restrictive: you need as the direct object the person (or thing)
which is being "let" do something.  This is followed by the infinitive
without the "to":

  o - "the `pred' pattern form lets you simplify ...."

> But it is usually better still to put the user first, as the active
> subject of the action:

Yes, perhaps, sometimes, maybe, ....

>   You can use `pred' to ...

> or if it's not clear what `pred' is:

>   You can use pattern `pred' to ...

> or:

>   You can simplify ... using `pred'.

> The user is the actor; pattern `pred' is the tool.

> (I did not look at the original, but a guess might be that
> someone reading Info should see PRED here (no quotes), not
> `pred'.

You're wrong here.  :-)  `pred' is an actual keyword in the pcase
mechanism.  (What exactly is the English for the German "Konzept"?
That's what I really wanted at the end of that sentence.)

> Another guess, again without consulting the text, is that perhaps you
> don't need both "pattern" and "form": "pattern form".)

I think they're both meaningful - the "pattern" is a description of what
is being matched, and the "form" is the lisp form which achieves this
matching.  I think.

> And I too thank Michael for helping us (all) understand the
> obtuse creature that is `pcase'.  It is comforting (and all
> too rare, it seems) to see such careful interest in helping
> Emacs document itself.  Kudos - very helpful.

Very helpful indeed!

-- 
Alan Mackenzie (Nuremberg, Germany).



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

* RE: Question on pcase
  2015-10-24 17:22                   ` Alan Mackenzie
@ 2015-10-24 17:36                     ` Drew Adams
  2015-10-24 20:03                     ` Johan Bockgård
  1 sibling, 0 replies; 51+ messages in thread
From: Drew Adams @ 2015-10-24 17:36 UTC (permalink / raw)
  To: Alan Mackenzie
  Cc: Michael Heerdegen, Eli Zaretskii, Oleh Krehel, Emacs Development

> > (I did not look at the original, but a guess might be that
> > someone reading Info should see PRED here (no quotes), not
> > `pred'.
> 
> You're wrong here.  :-)  `pred' is an actual keyword in the
> pcase mechanism.

OK.  I guessed wrong.

> > Another guess, again without consulting the text, is that perhaps you
> > don't need both "pattern" and "form": "pattern form".)
> 
> I think they're both meaningful - the "pattern" is a description of what
> is being matched, and the "form" is the lisp form which achieves this
> matching.  I think.

OK again.  I was (mis)guessing that "form" just referred here
to a piece of the pattern - a subpattern.

> > And I too thank Michael for helping us (all) understand the
> > obtuse creature that is `pcase'.  It is comforting (and all
> > too rare, it seems) to see such careful interest in helping
> > Emacs document itself.  Kudos - very helpful.
> 
> Very helpful indeed!



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

* Re: Question on pcase
  2015-10-24 12:58                 ` Stephen Berman
@ 2015-10-24 17:47                   ` Alan Mackenzie
  2015-10-24 19:10                     ` Stephen Berman
  0 siblings, 1 reply; 51+ messages in thread
From: Alan Mackenzie @ 2015-10-24 17:47 UTC (permalink / raw)
  To: Stephen Berman
  Cc: Michael Heerdegen, Eli Zaretskii, Oleh Krehel, Emacs Development

Hello, Stephen.

On Sat, Oct 24, 2015 at 02:58:54PM +0200, Stephen Berman wrote:
> On Sat, 24 Oct 2015 09:01:46 +0000 Alan Mackenzie <acm@muc.de> wrote:

> > 2. "Semantics", as a noun, is always plural in English (a bit like
> > "Eltern" in German).

> It's morphologically plural but syntactically singular, like "physics"
> -- at least in American English, but I think also in British English; or
> would you really say "Semantics are the study of meaning" or "Semantics
> are a science, and physics are, too"?  Hence, I think in

Yes, my mistake.

> > -;; All yet to understand is the semantic each of the basic PATTERNs.
> > +;; All that remains to understand are the semantics of each of the basic PATTERNs.
>                                      ^^^
> "are" should be replaced by "is".  (Even if "are" is acceptable here in
> British English, AFAIK the standard for GNU documentation is American
> English.)

Indeed, that "are" should be an "is".

> > -;; fulfils eihter `arrayp' or `numberp'.  And 3 if the binding of x is
> > +;; fulfils either `arrayp' or `numberp'.  And 3 if the binding of x is
>       ^^^^^^^
> I believe "fulfills" is the usual spelling in American English.

I just looked that one up.  "fulfills" is indeed the usual American
English spelling, "fulfils" being the British English spelling.

> >  ;; This is a pattern form that allows you to match a pattern PAT
> >  ;; against an _arbitrary_ expression EXP.  This is not special,
> > -;; matching PAT is done as you have learned, just against the EXP you
> > +;; matching PAT is done as you have learned, just on the EXP you
> >  ;; specify there, and not the EXPRESSION given to pcase at top level.

> Why replace "against" with "on" here but not in the preceding line?  I
> think "against" is usual in this context.  A somewhat better formulation
> is this, IMO: "The only difference from the pattern matching you have
> learned is that PAT is matched against the EXP you specify here..."

Oh, no!  This is complicated, too.  Perhaps the best preposition for
"match" here is "with", on the grounds that harmony is expected rather
than a fight.  You might match one sporting team against another, but
you'd match the colour of an item of clothing with that of another.  Or
something like that.

> > -;; If you are used to understand grammers: the above description of
> > -;; `QPAT describes a quite simpel grammer.  You make like to try it
> > +;; If you are used to understanding grammars: the above description of
> > +;; `QPAT describes a quite simple grammar.  You might like to try it

> I think a somewhat better formulation is this: "If you are familiar with
> formal language grammars, the above description..."

Possibly.  But that's rewriting the original, rather than correcting it.

> Steve Berman

-- 
Alan Mackenzie (Nuremberg, Germany).



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

* Re: Question on pcase
  2015-10-24 17:47                   ` Alan Mackenzie
@ 2015-10-24 19:10                     ` Stephen Berman
  2015-10-24 19:28                       ` Alan Mackenzie
  0 siblings, 1 reply; 51+ messages in thread
From: Stephen Berman @ 2015-10-24 19:10 UTC (permalink / raw)
  To: Alan Mackenzie
  Cc: Michael Heerdegen, Eli Zaretskii, Oleh Krehel, Emacs Development

On Sat, 24 Oct 2015 17:47:19 +0000 Alan Mackenzie <acm@muc.de> wrote:

>> >  ;; This is a pattern form that allows you to match a pattern PAT
>> >  ;; against an _arbitrary_ expression EXP.  This is not special,
>> > -;; matching PAT is done as you have learned, just against the EXP you
>> > +;; matching PAT is done as you have learned, just on the EXP you
>> >  ;; specify there, and not the EXPRESSION given to pcase at top level.
>
>> Why replace "against" with "on" here but not in the preceding line?  I
>> think "against" is usual in this context.  A somewhat better formulation
>> is this, IMO: "The only difference from the pattern matching you have
>> learned is that PAT is matched against the EXP you specify here..."
>
> Oh, no!  This is complicated, too.  

It doesn't strike me as more complicated than the original, just a bit
less stilted (to my ears).

>                                     Perhaps the best preposition for
> "match" here is "with", on the grounds that harmony is expected rather
> than a fight.  You might match one sporting team against another, but
> you'd match the colour of an item of clothing with that of another.  Or
> something like that.

"Match with" sounds out of place here.  I think "match against" is
pretty much standard in the context of pattern matching in formal or
programming languages; cf. these quotes (among many similar) from
Friedl's _Mastering Regular Expressions_ (3rd ed): 

"A collating sequence allows you to match against those characters that
are made up of combinations of other characters." (p128)

"This means that multiple regexes can match against a string, in turn
[...]." (p130)

"Thus, when trying to match 「ORA」 against FLORAL, the first attempt at
the start of the string fails (since 「ORA」 can’t match FLO)." (p148)

"Let’s consider one way an engine might match 「to(nite|knight|night)」
against the text ‘...tonight...’." (p153)

"The regex-directed NFA engine might waste time attempting to match
different subexpressions against the same text [...]." (p156)

>> > -;; If you are used to understand grammers: the above description of
>> > -;; `QPAT describes a quite simpel grammer.  You make like to try it
>> > +;; If you are used to understanding grammars: the above description of
>> > +;; `QPAT describes a quite simple grammar.  You might like to try it
>
>> I think a somewhat better formulation is this: "If you are familiar with
>> formal language grammars, the above description..."
>
> Possibly.  But that's rewriting the original, rather than correcting it.

Well, "If you are used to understanding grammars" sounds peculiar to me,
and in the context seems to mean nothing else than "If you are familiar
with formal language grammars", so I do think it more a correction than
a rewrite.  But it's up to Michael Heerdegen or whoever will maintain
this document.

Steve Berman



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

* Re: Question on pcase
  2015-10-24 19:10                     ` Stephen Berman
@ 2015-10-24 19:28                       ` Alan Mackenzie
  2015-10-25  0:00                         ` pcase docstring tweaks (was: Question on pcase) Michael Heerdegen
  0 siblings, 1 reply; 51+ messages in thread
From: Alan Mackenzie @ 2015-10-24 19:28 UTC (permalink / raw)
  To: Stephen Berman
  Cc: Michael Heerdegen, Eli Zaretskii, Oleh Krehel, Emacs Development

Hello, Stephen.

On Sat, Oct 24, 2015 at 09:10:49PM +0200, Stephen Berman wrote:
> On Sat, 24 Oct 2015 17:47:19 +0000 Alan Mackenzie <acm@muc.de> wrote:

> >> >  ;; This is a pattern form that allows you to match a pattern PAT
> >> >  ;; against an _arbitrary_ expression EXP.  This is not special,
> >> > -;; matching PAT is done as you have learned, just against the EXP you
> >> > +;; matching PAT is done as you have learned, just on the EXP you
> >> >  ;; specify there, and not the EXPRESSION given to pcase at top level.

> >> Why replace "against" with "on" here but not in the preceding line?  I
> >> think "against" is usual in this context.  A somewhat better formulation
> >> is this, IMO: "The only difference from the pattern matching you have
> >> learned is that PAT is matched against the EXP you specify here..."

> > Oh, no!  This is complicated, too.  

> It doesn't strike me as more complicated than the original, just a bit
> less stilted (to my ears).

Sorry for that.  I meant the _topic_ is complicated.

> >                                     Perhaps the best preposition for
> > "match" here is "with", on the grounds that harmony is expected rather
> > than a fight.  You might match one sporting team against another, but
> > you'd match the colour of an item of clothing with that of another.  Or
> > something like that.

> "Match with" sounds out of place here.  I think "match against" is
> pretty much standard in the context of pattern matching in formal or
> programming languages; cf. these quotes (among many similar) from
> Friedl's _Mastering Regular Expressions_ (3rd ed): 

Maybe.  I confess here to being personally irritated by "match against".
I don't know why.

But I honestly think it's time for our discussion to cease.  We've
unwittingly descended into bikeshedding, and we're not going to be
helping Michael Heerdegen much with it.  But thanks for the discussion
anyway!

[ Paragraphs snipped, but read and largely accepted. ]

> Steve Berman

-- 
Alan Mackenzie (Nuremberg, Germany).



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

* Re: Question on pcase
  2015-10-24 17:22                   ` Alan Mackenzie
  2015-10-24 17:36                     ` Drew Adams
@ 2015-10-24 20:03                     ` Johan Bockgård
  2015-10-24 23:11                       ` Michael Heerdegen
  1 sibling, 1 reply; 51+ messages in thread
From: Johan Bockgård @ 2015-10-24 20:03 UTC (permalink / raw)
  To: emacs-devel

Alan Mackenzie <acm@muc.de> writes:

> I think they're both meaningful - the "pattern" is a description of what
> is being matched, and the "form" is the lisp form which achieves this
> matching.  I think.

But a "form" is a Lisp _expression_ that you can evaluate. So (pred ...)
is not a form (it is special syntax that is only meaningful inside
pcase).



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

* Re: Question on pcase
  2015-10-24 20:03                     ` Johan Bockgård
@ 2015-10-24 23:11                       ` Michael Heerdegen
  0 siblings, 0 replies; 51+ messages in thread
From: Michael Heerdegen @ 2015-10-24 23:11 UTC (permalink / raw)
  To: emacs-devel

Johan Bockgård <bojohan@gnu.org> writes:

> But a "form" is a Lisp _expression_ that you can evaluate. So (pred
> ...)  is not a form (it is special syntax that is only meaningful
> inside pcase).

FWIW the Lisp manual is not very clear whether this is (not) a form.  Is
the evaluation expected to produce a result (or is it allowed to raise
an error)?  I guess the only answer that makes sense is "no".

Anyway, "form" has a second, different meaning.  It is used for that
second meaning all over Emacs documentation, e.g. (just a random
example)

,----------------------------------------------------------------------
| defface is a Lisp macro in ‘custom.el’.
| 
| SPEC should be a "face spec", i.e., an alist of the form
| 
|    ((DISPLAY . ATTS)...)
`----------------------------------------------------------------------


Regards,

Michael.




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

* pcase docstring tweaks (was: Question on pcase)
  2015-10-24 19:28                       ` Alan Mackenzie
@ 2015-10-25  0:00                         ` Michael Heerdegen
  2015-10-27 14:54                           ` pcase docstring tweaks Michael Heerdegen
  0 siblings, 1 reply; 51+ messages in thread
From: Michael Heerdegen @ 2015-10-25  0:00 UTC (permalink / raw)
  To: Alan Mackenzie
  Cc: Eli Zaretskii, Stephen Berman, Oleh Krehel, Emacs Development

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

Alan Mackenzie <acm@muc.de> writes:

> But I honestly think it's time for our discussion to cease.  We've
> unwittingly descended into bikeshedding, and we're not going to be
> helping Michael Heerdegen much with it.  But thanks for the discussion
> anyway!

I can only thank everyone who joined the discussion as well!  I'll think
carefully about all comments, and make the corrections.

Let's continue with the `pcase' docstring.  I think it's already quite
good.  Here is a patch emphasizing that EXP is actually evaluated (I
think that is not evident) and some more or less cosmetic changes.  Is
it clear then?



[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: pcase.patch --]
[-- Type: text/x-diff, Size: 2989 bytes --]

*** /tmp/ediff17164yyt	2015-10-25 01:54:53.175155728 +0200
--- /home/micha/software/emacs/lisp/emacs-lisp/pcase.el	2015-10-25 00:35:23.999856799 +0200
***************
*** 107,113 ****
  
  ;;;###autoload
  (defmacro pcase (exp &rest cases)
!   "Perform ML-style pattern matching on EXP.
  CASES is a list of elements of the form (PATTERN CODE...).
  
  Patterns can take the following forms:
--- 107,113 ----
  
  ;;;###autoload
  (defmacro pcase (exp &rest cases)
!   "Eval EXP and perform ML-style pattern matching on that value.
  CASES is a list of elements of the form (PATTERN CODE...).
  
  Patterns can take the following forms:
***************
*** 115,123 ****
    SYMBOL	matches anything and binds it to SYMBOL.
    (or PAT...)	matches if any of the patterns matches.
    (and PAT...)	matches if all the patterns match.
!   \\='VAL		matches if the object is `equal' to VAL
    ATOM		is a shorthand for \\='ATOM.
! 		   ATOM can be a keyword, an integer, or a string.
    (pred FUN)	matches if FUN applied to the object returns non-nil.
    (guard BOOLEXP)	matches if BOOLEXP evaluates to non-nil.
    (let PAT EXP)	matches if EXP matches PAT.
--- 115,123 ----
    SYMBOL	matches anything and binds it to SYMBOL.
    (or PAT...)	matches if any of the patterns matches.
    (and PAT...)	matches if all the patterns match.
!   \\='VAL		matches if the object is `equal' to VAL.
    ATOM		is a shorthand for \\='ATOM.
! 		   when ATOM is a keyword, an integer, or a string.
    (pred FUN)	matches if FUN applied to the object returns non-nil.
    (guard BOOLEXP)	matches if BOOLEXP evaluates to non-nil.
    (let PAT EXP)	matches if EXP matches PAT.
***************
*** 131,141 ****
                          which is the value being matched.
  So a FUN of the form SYMBOL is equivalent to one of the form (FUN).
  FUN can refer to variables bound earlier in the pattern.
- FUN is assumed to be pure, i.e. it can be dropped if its result is not used,
- and two identical calls can be merged into one.
  E.g. you can match pairs where the cdr is larger than the car with a pattern
  like \\=`(,a . ,(pred (< a))) or, with more checks:
  \\=`(,(and a (pred numberp)) . ,(and (pred numberp) (pred (< a))))
  
  Additional patterns can be defined via `pcase-defmacro'.
  Currently, the following patterns are provided this way:"
--- 131,141 ----
                          which is the value being matched.
  So a FUN of the form SYMBOL is equivalent to one of the form (FUN).
  FUN can refer to variables bound earlier in the pattern.
  E.g. you can match pairs where the cdr is larger than the car with a pattern
  like \\=`(,a . ,(pred (< a))) or, with more checks:
  \\=`(,(and a (pred numberp)) . ,(and (pred numberp) (pred (< a))))
+ FUN is assumed to be pure, i.e. it can be dropped if its result is not used,
+ and two identical calls can be merged into one.
  
  Additional patterns can be defined via `pcase-defmacro'.
  Currently, the following patterns are provided this way:"

[-- Attachment #3: Type: text/plain, Size: 26 bytes --]



Regards,

Michael.






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

* Re: Question on pcase
  2015-10-23 18:38             ` Michael Heerdegen
                                 ` (3 preceding siblings ...)
  2015-10-24  9:01               ` Alan Mackenzie
@ 2015-10-26 15:55               ` Oleh Krehel
  2015-10-26 16:07                 ` Michael Heerdegen
                                   ` (2 more replies)
  4 siblings, 3 replies; 51+ messages in thread
From: Oleh Krehel @ 2015-10-26 15:55 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: Eli Zaretskii, Emacs Development

Hi Micheal,

Thanks for your work, it could be useful to many people.

Michael Heerdegen <michael_heerdegen@web.de> writes:

> Someone who wants to try to learn pcase (Oleh? Oleh!) can help by
> reading it and telling me if it is understandable, and send corrections
> - or write a better introduction ;-) - and format it nicely for
> inclusion into Elpa or Emacs when it turns out to help people.

The reason I dislike `pcase' is not because I don't know how to use it
(the basic rules are actually pretty simple), it's because I think it
leads to code that's hard to understand, maintain and transform. I
dislike the trivial `if-let' and `when-let' for the same reasons.

I generally dislike any custom macro that includes `if' or binds
variables. This is because I can't reason about the code that uses these
macros unless I know exactly what they do in terms of binding variables
and selecting branches. These macros don't follow the substitution model
for procedure application (SMPA) [1], which is a valuable debugging
technique for me.

However, I'm willing to implement some tooling that will allow `pcase'
to follow SMPA.

Take this code for example, with "|" being the point:

(pcase which
  (`all t)
  |(`safe (member fun completion--capf-safe-funs))
  (`optimist (not (member fun completion--capf-misbehave-funs))))

Here, the (`all t) branch is selected, and the `which' symbol
can be extracted from the context with `up-list'.  I've implemented a
function that prints "pcase: nil" if the selected branch doesn't match,
and "pcase: t" when it matches.

This way, when I'm debugging a code with `pcase' I can see which branch
is the correct one without evaluating the code inside the branch.  After
this, I can step into the correct branch and use SMPA.

Of course, this function would also need to bind the same variables that
a `pcase' branch would bind.

Taking your example:

(pcase x
  ('a       1)
  ("Hallo"  2)
  |(thing    (message "%s is neither equal to 'a nor to \"Hallo\"." thing)))

This is what I would like to have:

(equal (macroexpand '(eval-pcase-branch x
                      (thing (message "%s is neither equal to 'a nor to \"Hallo\"." thing))))
       '(progn
         (setq thing x)
         (message "pcase: t")))

After this, the inner body of the branch can be properly evaluated,
since `thing' is bound to `x' now.

So far, I've implemented some code that can check if each branch will be
followed, see
https://github.com/abo-abo/lispy/commit/d3ed4e4fee435a2a448ddc0722d07cd997ee59d3.

But the code that binds the variables, e.g. (setq thing x) will likely
be hard to implement. For instance, look at this example with macroexpand:

(setq test '(1 . 2))
(pcase test
  (`(,foo . ,baz)
    (cons baz foo)))
;; =>
;; (2 . 1)

(macroexpand '(pcase test
               (`(,foo . ,baz)
                (cons baz foo))))
;; =>
;; (if (consp test)
;;     (let* ((x (car test))
;;            (x (cdr test)))
;;       (let ((baz x)
;;             (foo x))
;;         (cons baz foo)))
;;   nil)

The macroexpanded code returns (2 . 2) when evaluated. This I don't
understand. Although, it still works fine with `eval':

(eval (macroexpand '(pcase test
                     (`(,foo . ,baz)
                      (cons baz foo)))))
;; =>
;; (2 . 1)

Maybe someone could explain the above, and also suggest the best way the
create variable bindings from a pcase branch.

thanks again,
Oleh

[1]: https://mitpress.mit.edu/sicp/full-text/sicp/book/node10.html



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

* Re: Question on pcase
  2015-10-26 15:55               ` Oleh Krehel
@ 2015-10-26 16:07                 ` Michael Heerdegen
  2015-10-27  8:42                   ` Oleh Krehel
  2015-10-26 16:20                 ` Michael Heerdegen
  2015-10-26 16:35                 ` Andreas Schwab
  2 siblings, 1 reply; 51+ messages in thread
From: Michael Heerdegen @ 2015-10-26 16:07 UTC (permalink / raw)
  To: emacs-devel

Hi Oleh,

will read the rest of your post carefully later, but

> (macroexpand '(pcase test
>                (`(,foo . ,baz)
>                 (cons baz foo))))
> ;; =>
> ;; (if (consp test)
> ;;     (let* ((x (car test))
> ;;            (x (cdr test)))
> ;;       (let ((baz x)
> ;;             (foo x))
> ;;         (cons baz foo)))
> ;;   nil)
>
> The macroexpanded code returns (2 . 2) when evaluated. This I don't
> understand.

This is due to printing again.  With the default settings,

  (compose print read)

(so to say) is not guaranteed to preserve semantics of code.

Try with
 
  (setq print-gensym t)
  (setq print-circle t)

as I had suggested in a prior post.


Regards,

Michael.




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

* Re: Question on pcase
  2015-10-26 15:55               ` Oleh Krehel
  2015-10-26 16:07                 ` Michael Heerdegen
@ 2015-10-26 16:20                 ` Michael Heerdegen
  2015-10-27  8:42                   ` Oleh Krehel
  2015-10-26 16:35                 ` Andreas Schwab
  2 siblings, 1 reply; 51+ messages in thread
From: Michael Heerdegen @ 2015-10-26 16:20 UTC (permalink / raw)
  To: Oleh Krehel; +Cc: Eli Zaretskii, Emacs Development

Oleh Krehel <ohwoeowho@gmail.com> writes:

> The reason I dislike `pcase' is not because I don't know how to use it
> (the basic rules are actually pretty simple), it's because I think it
> leads to code that's hard to understand, maintain and transform. I
> dislike the trivial `if-let' and `when-let' for the same reasons.

I like them because I think they make code easier to understand and
maintain.

> I generally dislike any custom macro that includes `if' or binds
> variables. This is because I can't reason about the code that uses these
> macros unless I know exactly what they do in terms of binding variables
> and selecting branches. These macros don't follow the substitution model
> for procedure application (SMPA) [1], which is a valuable debugging
> technique for me.

I wonder how you debug.  With the built in debugger, with edebug,
something else?  Knowing that would help understanding you problem.

> Maybe someone could explain the above, and also suggest the best way the
> create variable bindings from a pcase branch.

`macroexpand' is your only choice if you want to have semantically
equivalent code you can actually run.  If arbitrary `pcase' forms could
be expanded to simpler code, well, we should rewrite `pcase' to expand
to this simpler code.


Michael.



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

* Re: Question on pcase
  2015-10-26 15:55               ` Oleh Krehel
  2015-10-26 16:07                 ` Michael Heerdegen
  2015-10-26 16:20                 ` Michael Heerdegen
@ 2015-10-26 16:35                 ` Andreas Schwab
  2 siblings, 0 replies; 51+ messages in thread
From: Andreas Schwab @ 2015-10-26 16:35 UTC (permalink / raw)
  To: Oleh Krehel; +Cc: Michael Heerdegen, Eli Zaretskii, Emacs Development

Oleh Krehel <ohwoeowho@gmail.com> writes:

> But the code that binds the variables, e.g. (setq thing x) will likely
> be hard to implement. For instance, look at this example with macroexpand:
>
> (setq test '(1 . 2))
> (pcase test
>   (`(,foo . ,baz)
>     (cons baz foo)))
> ;; =>
> ;; (2 . 1)
>
> (macroexpand '(pcase test
>                (`(,foo . ,baz)
>                 (cons baz foo))))
> ;; =>
> ;; (if (consp test)
> ;;     (let* ((x (car test))
> ;;            (x (cdr test)))
> ;;       (let ((baz x)
> ;;             (foo x))
> ;;         (cons baz foo)))
> ;;   nil)
>
> The macroexpanded code returns (2 . 2) when evaluated. This I don't
> understand.

Try binding print-gensym and print-circle to non-nil while the form is
printed.

Andreas.

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

* Re: Question on pcase
  2015-10-26 16:07                 ` Michael Heerdegen
@ 2015-10-27  8:42                   ` Oleh Krehel
  0 siblings, 0 replies; 51+ messages in thread
From: Oleh Krehel @ 2015-10-27  8:42 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: emacs-devel

Michael Heerdegen <michael_heerdegen@web.de> writes:

> Try with
>  
>   (setq print-gensym t)
>   (setq print-circle t)

Oops, missed that one. Now it works, but the macro expansion is a bit clumsy:

(if (consp test)
    (let* ((#2=#:x (car test))
           (#1=#:x (cdr test)))
      (let ((baz #1#)
            (foo #2#))
        (cons baz foo))) nil)

It would be great if all bound named variables were in their own block,
always in the same place. Then it would be very easy to extract them.



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

* Re: Question on pcase
  2015-10-26 16:20                 ` Michael Heerdegen
@ 2015-10-27  8:42                   ` Oleh Krehel
  2015-10-27 14:27                     ` Michael Heerdegen
  0 siblings, 1 reply; 51+ messages in thread
From: Oleh Krehel @ 2015-10-27  8:42 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: Eli Zaretskii, Emacs Development

Michael Heerdegen <michael_heerdegen@web.de> writes:

> Oleh Krehel <ohwoeowho@gmail.com> writes:
>
>> The reason I dislike `pcase' is not because I don't know how to use it
>> (the basic rules are actually pretty simple), it's because I think it
>> leads to code that's hard to understand, maintain and transform. I
>> dislike the trivial `if-let' and `when-let' for the same reasons.
>
> I like them because I think they make code easier to understand and
> maintain.

We can agree to disagree on this point. If I get an option to step into
a `pcase', I'll be able to work with it just as well as with
`cond'. I'll still never use it, but that's not the point. The point is
that I have to interact with `pcase' code if I want to edit the Emacs
core.

>> I generally dislike any custom macro that includes `if' or binds
>> variables. This is because I can't reason about the code that uses these
>> macros unless I know exactly what they do in terms of binding variables
>> and selecting branches. These macros don't follow the substitution model
>> for procedure application (SMPA) [1], which is a valuable debugging
>> technique for me.
>
> I wonder how you debug.  With the built in debugger, with edebug,
> something else?  Knowing that would help understanding you problem.

I debug with lispy [1], my package for Elisp and stuff. It's just a
sophisticated "C-x C-e" - all the program stack is in the global
variables.  If you'd like to try it, simply press "p" on consecutive
sexps to debug, e.g. "pjpjp". You can set up an initial stack with
`edebug': just "xe" on a function, evaluate something that calls it to
trigger `edebug' and press "Z".

>> Maybe someone could explain the above, and also suggest the best way the
>> create variable bindings from a pcase branch.
>
> `macroexpand' is your only choice if you want to have semantically
> equivalent code you can actually run.

`macroexpand' is the equivalent of stepping into a function call, when
you want to just call the function instead and examine the return
result. That's exactly my point about SMPA: you can only
eval-and-replace this whole huge `pcase' construct at once, you can't
decompose and evaluate it by parts. This should be done better.

> If arbitrary `pcase' forms could be expanded to simpler code, well, we
> should rewrite `pcase' to expand to this simpler code.

I'd prefer "we should use the simpler code", but your suggestion is a
step in the right direction as well :)

thanks,
Oleh

[1]: https://github.com/abo-abo/lispy



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

* Re: Question on pcase
  2015-10-27  8:42                   ` Oleh Krehel
@ 2015-10-27 14:27                     ` Michael Heerdegen
  2015-10-27 14:47                       ` Michael Heerdegen
  0 siblings, 1 reply; 51+ messages in thread
From: Michael Heerdegen @ 2015-10-27 14:27 UTC (permalink / raw)
  To: emacs-devel

Oleh Krehel <ohwoeowho@gmail.com> writes:

> I debug with lispy [1], my package for Elisp and stuff. It's just a
> sophisticated "C-x C-e" - all the program stack is in the global
> variables.

Ah, in global variables.  Now I understand why you wanted to use `setq'.

Hmm, as I said before, it's impossible in the general case to separate a
`pcase' pattern into a side effect free matching part and a
binding/setting part.  That's because when variable binding is done,
bindings established while performing the matching need to be in effect;
look at the example at the end to see what I mean.

So, the best I can offer is a function that transforms a PATTERN into a
function F that takes one argument EXP, the expression to be matched.  F
returns non-nil when the PATTERN would match EXP, else nil.  When the
PATTERN matches, all bindings created as a side effect from matching
that could be referred to in the CODE part of a (PATTERN CODE) `pcase'
branch are set with `setq'.

--8<---------------cut here---------------start------------->8---
;; -*- lexical-binding: t -*-
(defun abo-transform-pcase-pattern (pattern)
  (let ((arg (make-symbol "expression")))
    `(lambda (,arg) ,(pcase--u
                 `((,(pcase--match arg (pcase--macroexpand pattern))
                    ,(lambda (vars)
                       `(progn ,@(mapcar (lambda (b) `(setq ,(car b) ,(cdr b)))
                                         vars)))))))))
--8<---------------cut here---------------end--------------->8---

Because this is a function, you need to quote the PATTERN.

Example: transform the pattern `(,a ,b)

(abo-transform-pcase-pattern '`(,a ,b))

==>

(lambda
  (#1=#:expression)
  (if
      (consp #1#)
      (let*
          ((#5=#:x
            (car #1#))
           (#2=#:x
            (cdr #1#)))
        (if
            (consp #2#)
            (let*
                ((#4=#:x
                  (car #2#))
                 (#3=#:x
                  (cdr #2#)))
              (if
                  (null #3#)
                  (progn
                    (setq b #4#)
                    (setq a #5#))
                nil))
          nil))
    nil))


Regards,

Michael.




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

* Re: Question on pcase
  2015-10-27 14:27                     ` Michael Heerdegen
@ 2015-10-27 14:47                       ` Michael Heerdegen
  2015-10-28 18:05                         ` Michael Heerdegen
  0 siblings, 1 reply; 51+ messages in thread
From: Michael Heerdegen @ 2015-10-27 14:47 UTC (permalink / raw)
  To: emacs-devel

Michael Heerdegen <michael_heerdegen@web.de> writes:

> Hmm, as I said before, it's impossible in the general case to separate a
> `pcase' pattern into a side effect free matching part and a
> binding/setting part.  That's because when variable binding is done,
> bindings established while performing the matching need to be in effect;
> look at the example at the end to see what I mean.

Of course could you use temporary vars to delay the actual settings
until you want to establish them.  However, the computation of the
values that will be bound can't be separated from the predicate F that
is returned by `abo-transform-pcase-pattern', unless you want to rerun
the matching code with all potential side effects.

But AFAIU `abo-transform-pcase-pattern' should be sufficient for your
purpose.


Michael.




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

* Re: pcase docstring tweaks
  2015-10-25  0:00                         ` pcase docstring tweaks (was: Question on pcase) Michael Heerdegen
@ 2015-10-27 14:54                           ` Michael Heerdegen
  2015-10-27 18:57                             ` Stefan Monnier
  2015-10-28  3:15                             ` Richard Stallman
  0 siblings, 2 replies; 51+ messages in thread
From: Michael Heerdegen @ 2015-10-27 14:54 UTC (permalink / raw)
  To: Alan Mackenzie
  Cc: Eli Zaretskii, Stephen Berman, Oleh Krehel, Stefan Monnier,
	Emacs Development

Hello,

Stefan, ok to install this?

And as I said before: anyone is invited to bike shedding ;-)

> Let's continue with the `pcase' docstring.  I think it's already quite
> good.  Here is a patch emphasizing that EXP is actually evaluated (I
> think that is not evident) and some more or less cosmetic changes.  Is
> it clear then?
>
>
> *** /tmp/ediff17164yyt	2015-10-25 01:54:53.175155728 +0200
> --- /home/micha/software/emacs/lisp/emacs-lisp/pcase.el	2015-10-25 00:35:23.999856799 +0200
> ***************
> *** 107,113 ****
>   
>   ;;;###autoload
>   (defmacro pcase (exp &rest cases)
> !   "Perform ML-style pattern matching on EXP.
>   CASES is a list of elements of the form (PATTERN CODE...).
>   
>   Patterns can take the following forms:
> --- 107,113 ----
>   
>   ;;;###autoload
>   (defmacro pcase (exp &rest cases)
> !   "Eval EXP and perform ML-style pattern matching on that value.
>   CASES is a list of elements of the form (PATTERN CODE...).
>   
>   Patterns can take the following forms:
> ***************
> *** 115,123 ****
>     SYMBOL	matches anything and binds it to SYMBOL.
>     (or PAT...)	matches if any of the patterns matches.
>     (and PAT...)	matches if all the patterns match.
> !   \\='VAL		matches if the object is `equal' to VAL
>     ATOM		is a shorthand for \\='ATOM.
> ! 		   ATOM can be a keyword, an integer, or a string.
>     (pred FUN)	matches if FUN applied to the object returns non-nil.
>     (guard BOOLEXP)	matches if BOOLEXP evaluates to non-nil.
>     (let PAT EXP)	matches if EXP matches PAT.
> --- 115,123 ----
>     SYMBOL	matches anything and binds it to SYMBOL.
>     (or PAT...)	matches if any of the patterns matches.
>     (and PAT...)	matches if all the patterns match.
> !   \\='VAL		matches if the object is `equal' to VAL.
>     ATOM		is a shorthand for \\='ATOM.
> ! 		   when ATOM is a keyword, an integer, or a string.
>     (pred FUN)	matches if FUN applied to the object returns non-nil.
>     (guard BOOLEXP)	matches if BOOLEXP evaluates to non-nil.
>     (let PAT EXP)	matches if EXP matches PAT.
> ***************
> *** 131,141 ****
>                           which is the value being matched.
>   So a FUN of the form SYMBOL is equivalent to one of the form (FUN).
>   FUN can refer to variables bound earlier in the pattern.
> - FUN is assumed to be pure, i.e. it can be dropped if its result is not used,
> - and two identical calls can be merged into one.
>   E.g. you can match pairs where the cdr is larger than the car with a pattern
>   like \\=`(,a . ,(pred (< a))) or, with more checks:
>   \\=`(,(and a (pred numberp)) . ,(and (pred numberp) (pred (< a))))
>   
>   Additional patterns can be defined via `pcase-defmacro'.
>   Currently, the following patterns are provided this way:"
> --- 131,141 ----
>                           which is the value being matched.
>   So a FUN of the form SYMBOL is equivalent to one of the form (FUN).
>   FUN can refer to variables bound earlier in the pattern.
>   E.g. you can match pairs where the cdr is larger than the car with a pattern
>   like \\=`(,a . ,(pred (< a))) or, with more checks:
>   \\=`(,(and a (pred numberp)) . ,(and (pred numberp) (pred (< a))))
> + FUN is assumed to be pure, i.e. it can be dropped if its result is not used,
> + and two identical calls can be merged into one.
>   
>   Additional patterns can be defined via `pcase-defmacro'.
>   Currently, the following patterns are provided this way:"


Thanks,

Michael.



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

* Re: pcase docstring tweaks
  2015-10-27 14:54                           ` pcase docstring tweaks Michael Heerdegen
@ 2015-10-27 18:57                             ` Stefan Monnier
  2015-10-27 20:14                               ` Michael Heerdegen
  2015-10-28  3:15                             ` Richard Stallman
  1 sibling, 1 reply; 51+ messages in thread
From: Stefan Monnier @ 2015-10-27 18:57 UTC (permalink / raw)
  To: Michael Heerdegen
  Cc: Alan Mackenzie, Eli Zaretskii, Stephen Berman, Oleh Krehel,
	Emacs Development

>> ***************
>> *** 115,123 ****
>>   SYMBOL	matches anything and binds it to SYMBOL.
>>   (or PAT...)	matches if any of the patterns matches.
>>   (and PAT...)	matches if all the patterns match.
>> !   \\='VAL		matches if the object is `equal' to VAL
>>   ATOM		is a shorthand for \\='ATOM.
>> ! 		   ATOM can be a keyword, an integer, or a string.
>>   (pred FUN)	matches if FUN applied to the object returns non-nil.
>>   (guard BOOLEXP)	matches if BOOLEXP evaluates to non-nil.
>>   (let PAT EXP)	matches if EXP matches PAT.
>> --- 115,123 ----
>>   SYMBOL	matches anything and binds it to SYMBOL.
>>   (or PAT...)	matches if any of the patterns matches.
>>   (and PAT...)	matches if all the patterns match.
>> !   \\='VAL		matches if the object is `equal' to VAL.
>>   ATOM		is a shorthand for \\='ATOM.
>> ! 		   when ATOM is a keyword, an integer, or a string.
>>   (pred FUN)	matches if FUN applied to the object returns non-nil.
>>   (guard BOOLEXP)	matches if BOOLEXP evaluates to non-nil.
>>   (let PAT EXP)	matches if EXP matches PAT.

The ATOM change looks like a mild regression to me, but if you feel
strongly about that bikeshed color, feel free to change it (but then
please remove the "."  from the previous line, otherwise the "when"
doesn't continue anything).

The rest is fine.


        Stefan



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

* Re: pcase docstring tweaks
  2015-10-27 18:57                             ` Stefan Monnier
@ 2015-10-27 20:14                               ` Michael Heerdegen
  0 siblings, 0 replies; 51+ messages in thread
From: Michael Heerdegen @ 2015-10-27 20:14 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Emacs Development

Stefan Monnier <monnier@iro.umontreal.ca> writes:

> The ATOM change looks like a mild regression to me [...] bikeshed
> color [...]

Ok, then I'll leave this part as is.

> The rest is fine.

Thanks for reading.


Michael.



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

* Re: Question on pcase
  2015-10-23 13:59               ` Michael Heerdegen
@ 2015-10-27 23:50                 ` Johan Bockgård
  2015-10-30  1:33                   ` Michael Heerdegen
  0 siblings, 1 reply; 51+ messages in thread
From: Johan Bockgård @ 2015-10-27 23:50 UTC (permalink / raw)
  To: emacs-devel

Michael Heerdegen <michael_heerdegen@web.de> writes:

> The only thing that makes sense to me would be to make `edebug' let you
> step into a pattern.  AFAICT that would be a very hard work.

Edebug already understands most of pcase's patterns, but it doesn't step
"into" the SYMBOL bindings.

Try this:

    pcase.el: Support edebug stepping of SYMBOL patterns

    * lisp/emacs-lisp/pcase.el (pcase-SYMBOL) New edebug spec.
    (pcase-PAT): Use it.
    (pcase--edebug-match-symbol): New function.
    (pcase-QPAT): Improve handling of cons patterns.

diff --git a/lisp/emacs-lisp/pcase.el b/lisp/emacs-lisp/pcase.el
index 8bcb447..3ea519c 100644
--- a/lisp/emacs-lisp/pcase.el
+++ b/lisp/emacs-lisp/pcase.el
@@ -70,31 +70,20 @@ (defconst pcase--dontcare-upats '(t _ pcase--dontcare))
 
 (defvar pcase--dontwarn-upats '(pcase--dontcare))
 
-(def-edebug-spec
-  pcase-PAT
-  (&or symbolp
-       ("or" &rest pcase-PAT)
-       ("and" &rest pcase-PAT)
-       ("guard" form)
-       ("let" pcase-PAT form)
-       ("pred" pcase-FUN)
-       ("app" pcase-FUN pcase-PAT)
-       pcase-MACRO
-       sexp))
-
-(def-edebug-spec
-  pcase-FUN
-  (&or lambda-expr
-       ;; Punt on macros/special forms.
-       (functionp &rest form)
-       sexp))
-
-(def-edebug-spec pcase-MACRO pcase--edebug-match-macro)
-
 ;; Only called from edebug.
 (declare-function get-edebug-spec "edebug" (symbol))
 (declare-function edebug-match "edebug" (cursor specs))
+(declare-function edebug-no-match "edebug" (cursor &rest args))
+(declare-function edebug-top-element-required "edebug" (cursor &rest error))
 
+(def-edebug-spec pcase-SYMBOL pcase--edebug-match-symbol)
+(defun pcase--edebug-match-symbol (cursor)
+  (let ((sexp (edebug-top-element-required cursor "Expected" '(symbolp))))
+    (if (or (not (symbolp sexp)) (keywordp sexp))
+	(edebug-no-match cursor "Expected" '(symbolp))
+      (list `(and ,sexp (let _ ,(car (edebug-match cursor '(form)))))))))
+
+(def-edebug-spec pcase-MACRO pcase--edebug-match-macro)
 (defun pcase--edebug-match-macro (cursor)
   (let (specs)
     (mapatoms
@@ -105,6 +94,26 @@ (defun pcase--edebug-match-macro (cursor)
 		 specs)))))
     (edebug-match cursor (cons '&or specs))))
 
+(def-edebug-spec
+  pcase-FUN
+  (&or lambda-expr
+       ;; Punt on macros/special forms.
+       (functionp &rest form)
+       sexp))
+
+(def-edebug-spec
+  pcase-PAT
+  (&or "_"
+       pcase-SYMBOL
+       ("or" &rest pcase-PAT)
+       ("and" &rest pcase-PAT)
+       ("guard" form)
+       ("let" pcase-PAT form)
+       ("pred" pcase-FUN)
+       ("app" pcase-FUN pcase-PAT)
+       pcase-MACRO
+       sexp))
+
 ;;;###autoload
 (defmacro pcase (exp &rest cases)
   "Perform ML-style pattern matching on EXP.
@@ -865,8 +874,10 @@ (defun pcase--u1 (matches code vars rest)
 
 (def-edebug-spec
   pcase-QPAT
+  ;; Cf. edebug spec for `backquote-form' in edebug.el.
   (&or ("," pcase-PAT)
-       (pcase-QPAT . pcase-QPAT)
+       (pcase-QPAT [&rest [&not ","] pcase-QPAT]
+		   . [&or nil pcase-QPAT])
        (vector &rest pcase-QPAT)
        sexp))
 



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

* Re: pcase docstring tweaks
  2015-10-27 14:54                           ` pcase docstring tweaks Michael Heerdegen
  2015-10-27 18:57                             ` Stefan Monnier
@ 2015-10-28  3:15                             ` Richard Stallman
  2015-10-28 17:08                               ` Michael Heerdegen
  1 sibling, 1 reply; 51+ messages in thread
From: Richard Stallman @ 2015-10-28  3:15 UTC (permalink / raw)
  To: Michael Heerdegen
  Cc: emacs-devel, ohwoeowho, acm, eliz, stephen.berman, monnier

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

  > And as I said before: anyone is invited to bike shedding ;-)

How do you plan to shed the bikes?

-- 
Dr Richard Stallman
President, Free Software Foundation (gnu.org, fsf.org)
Internet Hall-of-Famer (internethalloffame.org)
Skype: No way! See stallman.org/skype.html.




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

* Re: pcase docstring tweaks
  2015-10-28  3:15                             ` Richard Stallman
@ 2015-10-28 17:08                               ` Michael Heerdegen
  0 siblings, 0 replies; 51+ messages in thread
From: Michael Heerdegen @ 2015-10-28 17:08 UTC (permalink / raw)
  To: emacs-devel

Richard Stallman <rms@gnu.org> writes:

> How do you plan to shed the bikes?

Dunno yet.  Do you want one?


Michael.




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

* Re: Question on pcase
  2015-10-27 14:47                       ` Michael Heerdegen
@ 2015-10-28 18:05                         ` Michael Heerdegen
  2015-10-29  9:44                           ` Oleh Krehel
  0 siblings, 1 reply; 51+ messages in thread
From: Michael Heerdegen @ 2015-10-28 18:05 UTC (permalink / raw)
  To: emacs-devel

Michael Heerdegen <michael_heerdegen@web.de> writes:

> But AFAIU `abo-transform-pcase-pattern' should be sufficient for your
> purpose.

Here is a version that should fit your use case better:

--8<---------------cut here---------------start------------->8---
;; -*- lexical-binding: t -*-

(defun abo-abo-pattern-matcher (pattern)
  "Turn pcase PATTERN into a predicate.
For any given pcase PATTERN, return a predicate P that returns
non-nil for any EXP when and only when PATTERN matches EXP.  In
that case, P returns a list of the form (bindings . BINDINGS) as
non-nil value, where BINDINGS is a list of bindings that pattern
matching with PATTERN would actually establish in a pcase branch."
  (let ((arg (make-symbol "exp")))
    `(lambda (,arg)
       ,(pcase--u
         `((,(pcase--match arg (pcase--macroexpand pattern))
            ,(lambda (vars)
               `(cons
                 'bindings
                 (list
                  ,@(nreverse (mapcar
                               (lambda (binding)
                                 `(cons ',(car binding)
                                        ,(cdr binding)))
                               vars)))))))))))
--8<---------------cut here---------------end--------------->8---


Example: Create a matcher predicate for the pattern

  `(,(and (pred integerp) x)
    ,(and (pred integerp)
          (pred (< 0))
           y))

and bind it to the variable `matcher':

(setq matcher
      (abo-abo-pattern-matcher
       '`(,(and (pred integerp) x)
          ,(and (pred integerp)
                (pred (< 0))
                y))))

  ==> 

--8<---------------cut here---------------start------------->8---
(lambda
  (#1=#:exp)
  (if
      (consp #1#)
      (let*
          ((#2=#:x
            (car #1#)))
        (if
            (integerp #2#)
            (let*
                ((#3=#:x
                  (cdr #1#)))
              (if
                  (consp #3#)
                  (let*
                      ((#4=#:x
                        (car #3#)))
                    (cond
                     ((not
                       (integerp #4#))
                      nil)
                     ((< 0 #4#)
                      (let*
                          ((#5=#:x
                            (cdr #3#)))
                        (if
                            (null #5#)
                            (cons 'bindings
                                  (list
                                   (cons 'x #2#)
                                   (cons 'y #4#)))
                          nil)))
                     (t nil)))
                nil))
          nil))
    nil))
--8<---------------cut here---------------end--------------->8---


(funcall matcher '(1 0))

  ==> nil

(funcall matcher '(1 2))

  ==> (bindings
       (x . 1) 
       (y . 2))


Regards,

Michael.








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

* Re: Question on pcase
  2015-10-28 18:05                         ` Michael Heerdegen
@ 2015-10-29  9:44                           ` Oleh Krehel
  2015-10-30  1:11                             ` Michael Heerdegen
  0 siblings, 1 reply; 51+ messages in thread
From: Oleh Krehel @ 2015-10-29  9:44 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: emacs-devel

Michael Heerdegen <michael_heerdegen@web.de> writes:

> Here is a version that should fit your use case better:
>
> ;; -*- lexical-binding: t -*-
>
> (defun abo-abo-pattern-matcher (pattern)
>   "Turn pcase PATTERN into a predicate.
> For any given pcase PATTERN, return a predicate P that returns
> non-nil for any EXP when and only when PATTERN matches EXP.  In
> that case, P returns a list of the form (bindings . BINDINGS) as
> non-nil value, where BINDINGS is a list of bindings that pattern
> matching with PATTERN would actually establish in a pcase branch."
>   (let ((arg (make-symbol "exp")))
>     `(lambda (,arg)
>        ,(pcase--u
>          `((,(pcase--match arg (pcase--macroexpand pattern))
>             ,(lambda (vars)
>                `(cons
>                  'bindings
>                  (list
>                   ,@(nreverse (mapcar
>                                (lambda (binding)
>                                  `(cons ',(car binding)
>                                         ,(cdr binding)))
>                                vars)))))))))))

Thanks a lot Michael!

This seems to be exactly what I want. It even works for the initial
example of debugging `completion-at-point' that I cited earlier:

(funcall
 (abo-abo-pattern-matcher
  '`(,hookfun . (,start ,end ,collection . ,plist)))
 res)
;; =>
;; (bindings (hookfun . elisp-completion-at-point)
;;           (start . 1841)
;;           (end . 1844)
;;           (collection . [...])
;;           (plist
;;            :predicate fboundp :company-doc-buffer elisp--company-doc-buffer
;;            :company-docsig elisp--company-doc-string
;;            :company-location elisp--company-location))

Now I can add the `pcase' debug functionality to lispy.  I don't know if
there's a place for this type of a helper function in the core. I think
that we should at least encourage `pcase' to macroexpand to a simple
`cond' with a regular structure every time.

regards,
Oleh



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

* Re: Question on pcase
  2015-10-29  9:44                           ` Oleh Krehel
@ 2015-10-30  1:11                             ` Michael Heerdegen
  0 siblings, 0 replies; 51+ messages in thread
From: Michael Heerdegen @ 2015-10-30  1:11 UTC (permalink / raw)
  To: emacs-devel

Oleh Krehel <ohwoeowho@gmail.com> writes:

> I think that we should at least encourage `pcase' to macroexpand to a
> simple `cond' with a regular structure every time.

If you mean to expand a single branch into a `cond' with a regular
structure: The semantics of `pcase' is too complex for that.  In
general, you have a decision _tree_, because patterns can depend on each
other, hence you can't expand to a `cond' form in the general case.
Instead, you have a tree of nested `if's, combined with let bindings
(that are used to construct the environment of bindings created by the
pattern parts matched "so far").


Michael.




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

* Re: Question on pcase
  2015-10-27 23:50                 ` Johan Bockgård
@ 2015-10-30  1:33                   ` Michael Heerdegen
  0 siblings, 0 replies; 51+ messages in thread
From: Michael Heerdegen @ 2015-10-30  1:33 UTC (permalink / raw)
  To: emacs-devel

Johan Bockgård <bojohan@gnu.org> writes:

> Edebug already understands most of pcase's patterns, but it doesn't step
> "into" the SYMBOL bindings.
>
> Try this:
>
>     pcase.el: Support edebug stepping of SYMBOL patterns
>
>     * lisp/emacs-lisp/pcase.el (pcase-SYMBOL) New edebug spec.
>     (pcase-PAT): Use it.
>     (pcase--edebug-match-symbol): New function.
>     (pcase-QPAT): Improve handling of cons patterns.

Very nice!

Do you think we could install this?  I don't feel familiar enough with
edebug to judge the quality of your patch, but I'm impressed that Edebug
can be made working for `pcase' with that few lines...


Thanks,

Michael.



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

end of thread, other threads:[~2015-10-30  1:33 UTC | newest]

Thread overview: 51+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-10-22 14:46 Question on pcase Oleh Krehel
2015-10-22 21:19 ` Michael Heerdegen
2015-10-23  6:30   ` Eli Zaretskii
2015-10-23 11:58     ` Oleh Krehel
2015-10-23 12:23       ` Michael Heerdegen
2015-10-23 12:42         ` Oleh Krehel
2015-10-23 13:07           ` Michael Heerdegen
2015-10-23 13:26             ` Oleh Krehel
2015-10-23 13:59               ` Michael Heerdegen
2015-10-27 23:50                 ` Johan Bockgård
2015-10-30  1:33                   ` Michael Heerdegen
2015-10-23 14:23               ` Michael Heerdegen
2015-10-23 12:17     ` Michael Heerdegen
2015-10-23 12:22       ` Oleh Krehel
2015-10-23 13:26       ` Eli Zaretskii
2015-10-23 14:14         ` Michael Heerdegen
2015-10-23 14:41           ` Eli Zaretskii
2015-10-23 18:38             ` Michael Heerdegen
2015-10-23 18:43               ` Eli Zaretskii
2015-10-23 19:59               ` Przemysław Wojnowski
2015-10-23 21:01                 ` Michael Heerdegen
2015-10-23 20:23               ` Przemysław Wojnowski
2015-10-23 20:39                 ` Michael Heerdegen
2015-10-24 11:37                   ` Przemysław Wojnowski
2015-10-24  9:01               ` Alan Mackenzie
2015-10-24 12:58                 ` Stephen Berman
2015-10-24 17:47                   ` Alan Mackenzie
2015-10-24 19:10                     ` Stephen Berman
2015-10-24 19:28                       ` Alan Mackenzie
2015-10-25  0:00                         ` pcase docstring tweaks (was: Question on pcase) Michael Heerdegen
2015-10-27 14:54                           ` pcase docstring tweaks Michael Heerdegen
2015-10-27 18:57                             ` Stefan Monnier
2015-10-27 20:14                               ` Michael Heerdegen
2015-10-28  3:15                             ` Richard Stallman
2015-10-28 17:08                               ` Michael Heerdegen
2015-10-24 17:00                 ` Question on pcase Drew Adams
2015-10-24 17:22                   ` Alan Mackenzie
2015-10-24 17:36                     ` Drew Adams
2015-10-24 20:03                     ` Johan Bockgård
2015-10-24 23:11                       ` Michael Heerdegen
2015-10-26 15:55               ` Oleh Krehel
2015-10-26 16:07                 ` Michael Heerdegen
2015-10-27  8:42                   ` Oleh Krehel
2015-10-26 16:20                 ` Michael Heerdegen
2015-10-27  8:42                   ` Oleh Krehel
2015-10-27 14:27                     ` Michael Heerdegen
2015-10-27 14:47                       ` Michael Heerdegen
2015-10-28 18:05                         ` Michael Heerdegen
2015-10-29  9:44                           ` Oleh Krehel
2015-10-30  1:11                             ` Michael Heerdegen
2015-10-26 16:35                 ` Andreas Schwab

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).