emacs-devel
[Top][All Lists]
Advanced

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

Re: What's missing in ELisp that makes people want to use cl-lib?


From: Dmitry Gutov
Subject: Re: What's missing in ELisp that makes people want to use cl-lib?
Date: Thu, 16 Nov 2023 23:54:01 +0200
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Thunderbird/102.13.0

Thanks, Gerd.

I think Stefan is not subscribed to this list anymore, so I'll Cc him in case he has something to add (or considers worth improving).

But cl-defmethod with just one method resulting in a simple function definition in an experimental fact (evaluate (cl-defmethod abc () 345); then (cl-defmethod abc () 345) returns (lambda nil (progn 345))), so that must be faster, without any computation of applicable methods.

The comment above the code you quoted mentions "generic functions with a single method"; maybe it was written before the above optimization was made.

On 16/11/2023 22:09, Gerd Möllmann wrote:
Gerd Möllmann<gerd.moellmann@gmail.com>  writes:

João Távora<joaotavora@gmail.com>  writes:

AFTER loading it

...
The magnitude of the difference when additional methods are defined I
find surprising. I take it as a strong indicator that cl-generic.el
indeed works completely differently than PCL. Assuming it is not a bug
af some sort. That the manual nowhere uses the term "discriminating
function" might also be a hint.
Looking a bit at cl-generics.el, at least the discriminating functions
part is indeed like nothing in PCL. And discriminating functions are not
in the manual, because there are none.

Disclaimer: I do not know cl-generics.el, and I've just looked enough to
get an impression.

So: I would describe the difference between PCL and cl-generic something
like static vs. dynamic, perhaps.

PCL goes to great lengths computing applicable methods etc. constructing
an as optimal as possible discriminating function. Once this has all
been done, nothing more has to be done at runtime. When methods are
changed, added etc. the whole thing is done from scratch again.

Cl-generics, in contrast, I'd say relies on runtime computation of
applicable methods and so on, plus memoization. If someone wants to see
what a generic function looks like, see cl-generic-define ->
cl--generic-make-function -> cl--generic-make-next-function ->
cl--generic-get-dispatcher. There we see

       (funcall
        cl--generic-compiler
        `(lambda (generic dispatches-left methods)
           (let ((method-cache (make-hash-table :test #'eql)))
             (lambda (,@fixedargs &rest args)
               (let ,bindings
                 (apply (with-memoization
                            (gethash ,tag-exp method-cache)
                          (cl--generic-cache-miss
                           generic ',dispatch-arg dispatches-left methods
                           ,(if (cdr typescodes)
                                `(append ,@typescodes) (car typescodes))))
                        ,@fixedargs args)))))))))

The hash-table is a cache, the inner lambda is the function definition
of the gf, the apply is the execution of an effective method, AFAIU. If
we hit an argument combination not in the cache, we compute applicable
methods at runtime, I believe.

A bit strange is that cl--generic-next-function seems to be called
recursively in the process, which I think could create another such
hash-table. Or I'm reading that simply wrong, as I mentioned I just
wanted to see if cl-generic is so different, so I didn't spend much time
on this.

The question how that leads to such-and-such performance effects I can't
answer. I haven't seen such an implementation before.

And I'm not saying it's bad! There are very very very (did I say very
enough?) good reasons to try and avoid the incredible complexity of
something like PCL.




reply via email to

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