emacs-devel
[Top][All Lists]
Advanced

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

Re: Instead of pcase


From: Barry Fishman
Subject: Re: Instead of pcase
Date: Sun, 19 Nov 2023 16:15:25 -0500
User-agent: Gnus/5.13 (Gnus v5.13)

On 2023-11-19 19:59:42 +02, Dmitry Gutov wrote:
> On 19/11/2023 18:02, Barry Fishman wrote:
>> On 2023-11-19 13:23:17 +01, Michael Heerdegen wrote:
>>> So, the advantage would be more of a psychological kind: you have normal
>>> Lisp function calls, but structurally you have the same thing, and you
>>> have to remember exactly the same set of details.  The complexity comes
>>> from the task, not from the implementation.
>> I submit that the opposite is true,  the complexity is in its
>> implementation, not the task.
>> In languages like Haskell this is expressed in a very simple way, so
>> the
>> task is not in itself hard to understand.
>
> Haskell is notable for using the same (or similar) syntax for
> destructuring as it uses for constructing expressions.
>
> pcase actually follows that practice with backquotes and commas.

Really?  I thought back-quoting was used (as an alternative way) to
construct nested lists, to express more simply the piecing together and
concatenating nested list pieces, particularly in macroes, and its
expansion at compile time is fairly obvious.

I guess what pcase is trying to do is the similar, but it is expressing
something that, to me, seem far more complex to use.

Its also mixing "destructuring" and "guard"s, all in one "super" domain
specific language.

Like loop, it can be powerful, but intimidating to people that don't use
it a lot.  But its usefulness to those that know it, encourages its
use in the code base.

But exiting Lisp destructuring via destructuring-bind and lambda
arguments, don't use backquoting.

One thing I did change was to reverse the defmethod style of (variable
type) to (type variable) to try to simplify recursion in the typeproto
syntax.

> And in both matching and destructuring are done in the same expression
> (the presence of the latter is determined by whether a pattern
> contains variable bindindgs).
>
>> If x is a list with its first element a string "foo" and the second an
>> integer, remove it from x and add the second element to total:
>>    (proto-case x
>>      ((list* (string name) (integer count) (t rest))
>>       (string-equal name "foo")
>>       (set! total (+ total count))
>>       (set! x rest)))
>> This is just one of many other ways one could setup a matching
>> expression in elisp, the best is a balance between implementation
>> efficiency and readability.
>
> And this divorces matching from destructuring. I suggest you try
> rewriting one of the more complex pcase usages into this syntax and
> see what people think of the result.

You want me to guess what you have in mind?  I have no idea.

I'll take the example pcase code from the Emacs manual:

(pcase (get-return-code x)
  ;; string
  ((and (pred stringp) msg) 
        (message "%s" msg))
  ;; symbol
  ('success       (message "Done!"))
  ('would-block   (message "Sorry, can't do it now"))
  ('read-only     (message "The shmliblick is read-only"))
  ('access-denied (message "You do not have the needed rights"))
  ;; default
  (code           (message "Unknown return code %S" code)))

This is a simple use case, so its hard to use to draw any conclusions.

This is mostly value handling for a known type, which is to me a
different problem than destructuring, and be handled better by a
generalized value case structure.  But still:

(let ((msglist
        '((success       . "Done!")
          (would-block   . "Sorry, can't do it now")
          (read-only     . "The shmliblick is read-only")
          (access-denied . "You do not have the needed rights"))))
  (proto-case (get-return-code x)
    ((string msg) t (message "%s" msg))
    ((symbol sym) t (let ((pair (assq sym msglist)))
                      (if pair
                        (message (cdr pair))
                        (message "Unknown return symbol %S" sym))))
    ((t code)     t (message "Unknown return type %S" code))))

In this situation pcase situation is a slightly better, to my
sensibility's. [Now that pcase is written]

One could also define a value oriented traditional style case:

 (vcase (x typecmp default)
     ((value expression ...) ...))

Which tests x using (funcall typecmp x value), and returns default
if there is no match.

And avoid the proto-case all together:

(let ((code (get-return-code)))
  (cond
   ((stringp code) (message "%s" msg))
   ((symbolp code)
    (or (vcase (sym eq nil)
          ('success       (message "Done!"))
          ('would-block   (message "Sorry, can't do it now"))
          ('read-only     (message "The shmliblick is read-only"))
          ('access-denied (message "You do not have the needed rights")))
        (message "Unknown return code %S" sym)))
   (t 
     (message "Unknown return type %S" code))))

-- 
Barry Fishman




reply via email to

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