[Top][All Lists]


Re: "Like `let*' but ....."

From: Stefan Monnier
Subject: Re: "Like `let*' but ....."
Date: Wed, 25 Jan 2017 00:08:01 -0500

> Those highly nested expressions come from destructuring.
> I think the expansion is nearly as effecient as it could be.

There's definitely room for improvement.  Some of it would be
easier/cleaner to fix in the byte-compiler than in the pcase macro.

The most costly part is the use of (funcall pcase-N ...).  Such forms
are used to avoid duplicating the body of a branch (in the case of
`pcase-let` that means duplicating the body of the `pcase-let`), so
they're important to avoid potential code size blow up, but they impose
a significant performance cost.  Luckily, in many cases we can avoid
them (e.g. when the body is small, or when the patterns are simple
enough that they don't lead to any duplication at all).

BTW, regarding the big pcase-let* in byte-compile-file-form-defalias,
it was partly an experiment in the use of pcase patterns.

OT1H I'm not convinced the result speaks very much in favor of pcase,
OTOH if you try to rewrite this code without pcase it's not
pretty either.

I'll read it for you:

           ;; `macro' is non-nil if it defines a macro.
           ;; `fun' is the function part of `arg' (defaults to `arg').
           (((or (and (or `(cons 'macro ,fun) `'(macro . ,fun)) (let macro t))
                 (and (let fun arg) (let macro nil)))

This part binds two vars: `fun` and `macro`.
Basically, it binds `fun` to the value of `arg` except that if `arg` is
a "macro value", `fun` gets the value of the macro's function (and
`macro` gets value t rather than nil).

            ;; `lam' is the lambda expression in `fun' (or nil if not
            ;; recognized).
            ((or `(,(or `quote `function) ,lam) (let lam nil))

This only binds `lam`.  It takes the `fun` apart and extracts a lambda
expression from it (or nil if it can't).

            ;; `arglist' is the list of arguments (or t if not recognized).
            ;; `body' is the body of `lam' (or t if not recognized).
            ((or `(lambda ,arglist . ,body)
                 ;; `(closure ,_ ,arglist . ,body)
                 (and `(internal-make-closure ,arglist . ,_) (let body t))
                 (and (let arglist t) (let body t)))

This tries to take apart `lam` and extract two parts: `arglist` and `body`.

The complexity here is that `arg` doesn't hold a "macro value"
(i.e. something of the form (macro . FUN)), but instead it holds an
expression that will return a macro value.  So `arg` can be of the form

    (quote (macro . FUN))
    (cons 'macro 'FUN)
    (cons 'macro #'FUN)
    (cons 'macro (internal-make-closure ...))

and of course FUN is usually of the form (lambda ...) but it can also be
a plain symbol, or a #<subr ...> object or god knows what else.


reply via email to

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