Rocksolid Light

Welcome to RetroBBS

mail  files  register  newsreader  groups  login

Message-ID:  

Are we running light with overbyte?


devel / comp.lang.lisp / Re: Implementation of call-next-method and next-method-p

SubjectAuthor
* Implementation of call-next-method and next-method-pStefan Monnier
`* Re: Implementation of call-next-method and next-method-pKaz Kylheku
 `* Re: Implementation of call-next-method and next-method-pKaz Kylheku
  `* Re: Implementation of call-next-method and next-method-pStefan Monnier
   `- Re: Implementation of call-next-method and next-method-pStefan Monnier

1
Implementation of call-next-method and next-method-p

<jwvlezz2q1f.fsf-monnier+comp.lang.lisp@gnu.org>

  copy mid

https://www.rocksolidbbs.com/devel/article-flat.php?id=16775&group=comp.lang.lisp#16775

  copy link   Newsgroups: comp.lang.lisp
Path: i2pn2.org!i2pn.org!aioe.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: monnier@iro.umontreal.ca (Stefan Monnier)
Newsgroups: comp.lang.lisp
Subject: Implementation of call-next-method and next-method-p
Date: Sat, 01 Jan 2022 15:40:20 -0500
Organization: A noiseless patient Spider
Lines: 101
Message-ID: <jwvlezz2q1f.fsf-monnier+comp.lang.lisp@gnu.org>
Mime-Version: 1.0
Content-Type: text/plain; charset=iso-8859-1
Content-Transfer-Encoding: 8bit
Injection-Info: reader02.eternal-september.org; posting-host="77ec411c490a72c70596c0a2080e7be0";
logging-data="22095"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX19f3y1FFKnLczff6hIHHTj+"
User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/29.0.50 (gnu/linux)
Cancel-Lock: sha1:k3pkiPui7hyWCvYjScOLi5kr1bc=
sha1:EAVFBv0mWkvSuMS2CWftRV7PRuA=
 by: Stefan Monnier - Sat, 1 Jan 2022 20:40 UTC

Hi,

I see both in Closette and in PCL that the implementation of
`next-method-p` boils down to (taken from Closette):

`(lambda (args next-emfun)
(flet ((call-next-method (&rest cnm-args)
(if (null next-emfun)
(error "No next method for the~@
generic function ~S."
(method-generic-function ',method))
(funcall next-emfun (or cnm-args args))))
(next-method-p ()
(not (null next-emfun))))
(apply #'(lambda ,(kludge-arglist lambda-list)
,form)
args))))))

I'm curious if that same approach is used in all other implementations
out there.

I'm specifically curious about this choice to represent (at run-time)
the "no next method" (NNM) case as nil, forcing an `if` test in
`call-nex-method`.

PCL makes a lot of effort to optimize some of this code away, by
specializing the case where CNM and NNM aren't used, or are only called
directly (in which case they can be inlined via `macrolet`), but it does
boil down to the same thing:

(call-next-method-body (cnm-args)
`(if .next-method.
(funcall (if (std-instance-p .next-method.)
(method-function .next-method.)
.next-method.) ; for early methods
(or ,cnm-args ,',method-args)
,',next-methods)
(error "No next method.")))
(next-method-p-body ()
`(not (null .next-method.))))

In ELisp, I used a quite simplistic approach, very similar to Closette,
but I made a different optimization (with no measurement to know whether
it was worth the effort) based on an ugly gross hack: basically the
"next method" is always a function (when there's no next method, it's
a function that calls `no-next-method`), so `call-next-method` can
directly call it without needing the `if` test.

The ugly gross hack comes up in `next-method-p` where we need to
distinguish the "functions that calls `no-next-method`" from the other
functions. We recognize those by making sure we build them from the
same place in the code, so all those closures share the same code which
makes it possible to recognize them more-or-less reliably by comparing
to a "sample" of that function (it all breaks down in cases like when we
re-evaluate the code that builds those closures, as happens during
development/debugging but it's worked well enough in practice).

So I'm curious to know if other Common Lisp implementation have made
similar optimizations, and hopefully found less hideous ways to do it.

Stefan

PS: For the curious, the ugly gross hack looks like the code below,
which is even more ugly and gross than strictly needed, because closure
we have to compare with the one that calls `no-next-method` is actually
stuffed within another wrapper which basically does (lambda (&rest args)
(apply FUN (or args original-args))):

(defconst cl--generic-nnm-sample (cl--generic-no-next-method-function t t))
(defconst cl--generic-cnm-sample
(funcall (cl--generic-build-combined-method
nil (list (cl--generic-make-method () () t #'identity)))))

(defun cl--generic-isnot-nnm-p (cnm)
"Return non-nil if CNM is the function that calls `cl-no-next-method'."
;; ¡Big Gross Ugly Hack!
;; `next-method-p' just sucks, we should let it die. But EIEIO did support
;; it, and some packages use it, so we need to support it.
(catch 'found
(cl-assert (function-equal cnm cl--generic-cnm-sample))
(if (byte-code-function-p cnm)
(let ((cnm-constants (aref cnm 2))
(sample-constants (aref cl--generic-cnm-sample 2)))
(dotimes (i (length sample-constants))
(when (function-equal (aref sample-constants i)
cl--generic-nnm-sample)
(throw 'found
(not (function-equal (aref cnm-constants i)
cl--generic-nnm-sample))))))
(cl-assert (eq 'closure (car-safe cl--generic-cnm-sample)))
(let ((cnm-env (cadr cnm)))
(dolist (vb (cadr cl--generic-cnm-sample))
(when (function-equal (cdr vb) cl--generic-nnm-sample)
(throw 'found
(not (function-equal (cdar cnm-env)
cl--generic-nnm-sample))))
(setq cnm-env (cdr cnm-env)))))
(error "Haven't found no-next-method-sample in cnm-sample")))

Re: Implementation of call-next-method and next-method-p

<20220102125251.953@kylheku.com>

  copy mid

https://www.rocksolidbbs.com/devel/article-flat.php?id=16777&group=comp.lang.lisp#16777

  copy link   Newsgroups: comp.lang.lisp
Path: i2pn2.org!i2pn.org!aioe.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: 480-992-1380@kylheku.com (Kaz Kylheku)
Newsgroups: comp.lang.lisp
Subject: Re: Implementation of call-next-method and next-method-p
Date: Sun, 2 Jan 2022 21:45:07 -0000 (UTC)
Organization: A noiseless patient Spider
Lines: 124
Message-ID: <20220102125251.953@kylheku.com>
References: <jwvlezz2q1f.fsf-monnier+comp.lang.lisp@gnu.org>
Injection-Date: Sun, 2 Jan 2022 21:45:07 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="1c77eac8857645371d7d665400fced1e";
logging-data="25298"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1+ybQLHLrASNLFaxhlqWeLxLUMz2yP+vBY="
User-Agent: slrn/1.0.3 (Linux)
Cancel-Lock: sha1:OGpedZQcmBCoMg0mCgSddvZyaJc=
 by: Kaz Kylheku - Sun, 2 Jan 2022 21:45 UTC

On 2022-01-01, Stefan Monnier <monnier@iro.umontreal.ca> wrote:
> Hi,
>
> I see both in Closette and in PCL that the implementation of
> `next-method-p` boils down to (taken from Closette):
>
> `(lambda (args next-emfun)
> (flet ((call-next-method (&rest cnm-args)
> (if (null next-emfun)
> (error "No next method for the~@
> generic function ~S."
> (method-generic-function ',method))
> (funcall next-emfun (or cnm-args args))))
> (next-method-p ()
> (not (null next-emfun))))
> (apply #'(lambda ,(kludge-arglist lambda-list)
> ,form)
> args))))))
>
> I'm curious if that same approach is used in all other implementations
> out there.

There has to be some function call-next-method bound somehow, and it
needs to know the next method from somewhere. That would seem to
constrain how it can work to more or less this sort of thing.

> I'm specifically curious about this choice to represent (at run-time)
> the "no next method" (NNM) case as nil, forcing an `if` test in
> `call-nex-method`.

Well, you need to be able to have a test to support next-method-p.

So suppose next-emfun is never null, such that it's a valid function
(which errors out if there is no next method).

Then next-method-p has to be able to know that it is that special
function. No problem, I suppose:

(next-method-p () (not (eq next-emfun) #'no-next-method-emfun))

That is going to backfire. If no-next-method-emfun is redefined,
simply by the containing module being reloaded, its EQ identity
will change. HOwever, it could be done like this:

(defun non-next-method-emfun () ...)

(defun non-next-method-emfunp (f) (not (eq f #'non-next-method-emfun)))

These functions sit in the same module. So if one is redefined, so is
the other.

I would still worry about some run-time mismatch. So that is to say,
some method dispatch is currently "live" and uses the old emfun, which
is dynamically redefined and so now the emfunp test doesn't recognize
it, having switched to the new one.

One way out is to have a secret argument to the emfun which it
recognizes and returns T or NIL.

(defun non-next-method-emfun (args)
(if (eq args 'sys:next-method-check)
nil ;; that's right, there is no next method
(error ...)))

Thus, we ask the function itself: are you the no-next-method
error function?

However, now all the method-dispatching wrappers have to be wrapped with
this test (and return T when args is sys:next-method-check), so I'm not
convinced we made things any better.

In TXR Lisp, I used a piece of somewhat like-minded trickery in the
implementation of delimited continuations.

If you pass the symbol sys:cont-poison to a continuation, it's a special
signal which means "please unwind the delimited continuation's stack
instead of resuming it".

Suppose our garbage collection supports some kinds of weak structures.

Imagine we have a "weak set" structure:

(defvar no-next-method-set (weak-set))

Then each time we redefine the no-next-method-emfun shim, we add it to
the set:

(defun no-next-method-emfun (args) ...)

(weak-set-add no-next-method-set #'no-next-method-emfun)

The redefinition of the function never takes place without adding the
new definition to the set, because they are adjacent top-level forms in
the same compiled file.

Then the no-next-method-p just does this:

(no-next-method-p () (weak-set-member-p no-next-method-set next-emfun))

At garbage collection time, the weak set will expunge all members which
are not reachable.

So with this we know whether the next-emfun is either the same function
as the current definition, or else a past definition (which is
necessarily still reachable, since that variable is referencing it, and
thus in the weak set).

I think the weak-set abstraction can be readily implemented in a Lisp
that has weak hash tables. The weak set members can simply be keys in in
a weak-key hash table, with T values.

> PCL makes a lot of effort to optimize some of this code away, by
> specializing the case where CNM and NNM aren't used, or are only called

That effort seems misplaced; eliminating unused flets is a pretty simple
optimization (and an important one, I'd expect Lisp compilers to have).

If a flet is not called, it can be deleted right at the abstract syntax
tree level. Like when you're generating code for flet, you check: does
this identifier have a use? If not, skip it. The use information can be
obtained even in a one-pass compiler by compiling the body first.

Maybe some important Lisps were missing that at the time PCL was
developed.

Re: Implementation of call-next-method and next-method-p

<20220102140139.897@kylheku.com>

  copy mid

https://www.rocksolidbbs.com/devel/article-flat.php?id=16778&group=comp.lang.lisp#16778

  copy link   Newsgroups: comp.lang.lisp
Path: i2pn2.org!i2pn.org!aioe.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: 480-992-1380@kylheku.com (Kaz Kylheku)
Newsgroups: comp.lang.lisp
Subject: Re: Implementation of call-next-method and next-method-p
Date: Sun, 2 Jan 2022 22:12:22 -0000 (UTC)
Organization: A noiseless patient Spider
Lines: 18
Message-ID: <20220102140139.897@kylheku.com>
References: <jwvlezz2q1f.fsf-monnier+comp.lang.lisp@gnu.org>
<20220102125251.953@kylheku.com>
Injection-Date: Sun, 2 Jan 2022 22:12:22 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="1c77eac8857645371d7d665400fced1e";
logging-data="31844"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1/gCsipncGcxtIit+JwkwOKgWGevnUr3NA="
User-Agent: slrn/1.0.3 (Linux)
Cancel-Lock: sha1:pcVFe2vHMHD1BRvzaNmPaYYiKg8=
 by: Kaz Kylheku - Sun, 2 Jan 2022 22:12 UTC

On 2022-01-02, Kaz Kylheku <480-992-1380@kylheku.com> wrote:
> That is going to backfire. If no-next-method-emfun is redefined,
> simply by the containing module being reloaded, its EQ identity
> will change. HOwever, it could be done like this:
>
> (defun non-next-method-emfun () ...)
>
> (defun non-next-method-emfunp (f) (not (eq f #'non-next-method-emfun)))
>
> These functions sit in the same module. So if one is redefined, so is
> the other.

I missed the obvious simplicity:

;; Defined once, never altered:
(defvar no-next-method-enfun (lambda (args) ...))

We just refer to that variable.

Re: Implementation of call-next-method and next-method-p

<jwvr19pxy34.fsf-monnier+comp.lang.lisp@gnu.org>

  copy mid

https://www.rocksolidbbs.com/devel/article-flat.php?id=16779&group=comp.lang.lisp#16779

  copy link   Newsgroups: comp.lang.lisp
Path: i2pn2.org!i2pn.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: monnier@iro.umontreal.ca (Stefan Monnier)
Newsgroups: comp.lang.lisp
Subject: Re: Implementation of call-next-method and next-method-p
Date: Sun, 02 Jan 2022 23:09:45 -0500
Organization: A noiseless patient Spider
Lines: 18
Message-ID: <jwvr19pxy34.fsf-monnier+comp.lang.lisp@gnu.org>
References: <jwvlezz2q1f.fsf-monnier+comp.lang.lisp@gnu.org>
<20220102125251.953@kylheku.com> <20220102140139.897@kylheku.com>
Mime-Version: 1.0
Content-Type: text/plain
Injection-Info: reader02.eternal-september.org; posting-host="b7c9c27485cff7b815b757057408e3e3";
logging-data="31257"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1/CwA6eYAqXPFn7zPXOSN8A"
User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/29.0.50 (gnu/linux)
Cancel-Lock: sha1:XMWkdl8TU/xJm8vLZKpk14gRDoQ=
sha1:FKOUl3a0FF6C/EKmSr26hId51tU=
 by: Stefan Monnier - Mon, 3 Jan 2022 04:09 UTC

> I missed the obvious simplicity:
>
> ;; Defined once, never altered:
> (defvar no-next-method-enfun (lambda (args) ...))
>
> We just refer to that variable.

When we call `call-next-method` and there's no next method, it ends up
calling `no-next-method` with a set of arguments which depends on the
current method call (it includes the name of the generic function, and
the arguments passed to it), so it can be a constant function.
In practice you can make it so all those functions are instances that
all come from the same lambda in the source code, tho, which is how
ELisp's implementation detects that it is one of the "call
no-next-method function".

Stefan

Re: Implementation of call-next-method and next-method-p

<jwvfsq5xx52.fsf-monnier+comp.lang.lisp@gnu.org>

  copy mid

https://www.rocksolidbbs.com/devel/article-flat.php?id=16780&group=comp.lang.lisp#16780

  copy link   Newsgroups: comp.lang.lisp
Path: i2pn2.org!i2pn.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: monnier@iro.umontreal.ca (Stefan Monnier)
Newsgroups: comp.lang.lisp
Subject: Re: Implementation of call-next-method and next-method-p
Date: Sun, 02 Jan 2022 23:26:57 -0500
Organization: A noiseless patient Spider
Lines: 10
Message-ID: <jwvfsq5xx52.fsf-monnier+comp.lang.lisp@gnu.org>
References: <jwvlezz2q1f.fsf-monnier+comp.lang.lisp@gnu.org>
<20220102125251.953@kylheku.com> <20220102140139.897@kylheku.com>
<jwvr19pxy34.fsf-monnier+comp.lang.lisp@gnu.org>
Mime-Version: 1.0
Content-Type: text/plain
Injection-Info: reader02.eternal-september.org; posting-host="b7c9c27485cff7b815b757057408e3e3";
logging-data="4244"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX19LS0CmGyB4f5vlFSyqMac8"
User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/29.0.50 (gnu/linux)
Cancel-Lock: sha1:hNL/eB5m3y4m160zB+Pr1+EWcQ0=
sha1:pi8eH3VrUY3CfIeUdOcGaGLotT0=
 by: Stefan Monnier - Mon, 3 Jan 2022 04:26 UTC

> When we call `call-next-method` and there's no next method, it ends up
> calling `no-next-method` with a set of arguments which depends on the
> current method call (it includes the name of the generic function, and
> the arguments passed to it), so it can be a constant function.
^^^
cannot
Duh!

Stefan


devel / comp.lang.lisp / Re: Implementation of call-next-method and next-method-p

1
server_pubkey.txt

rocksolid light 0.9.81
clearnet tor