From: "João Távora" <joaotavora@gmail.com>
To: Stefan Monnier <monnier@iro.umontreal.ca>
Cc: emacs-devel@gnu.org
Subject: Re: Attaching context info to an error
Date: Thu, 28 Dec 2023 14:12:09 +0000 [thread overview]
Message-ID: <CALDnm51gurztb0dr-XF+dX6dZUX83H0oz3S4kMvPL+fBe3fUEA@mail.gmail.com> (raw)
In-Reply-To: <jwvmstug7ev.fsf-monnier+emacs@gnu.org>
On Thu, Dec 28, 2023 at 7:05 AM Stefan Monnier <monnier@iro.umontreal.ca> wrote:
>
> >> >> [ Not clear what `initialize-instance` would be used for.
> >> > It is passed the keyword arguments that are passed to CL:ERROR
> >> > or CL:SIGNAL [1,2].
> >> Yes, I know, but that doesn't tell me what it lets us do that we can't
> >> do right now.
> > I guess we can do everything with symbol and cons,
>
> I don't mean "do" as in "Turing equivalent".
> "less work" is a good thing to "do". I just don't know in which way
> `initialize-instance` lets one do less work.
It's not just initialize-instance, of course, it's CLOS or an
equivalent. And what does that give us? Much that we are reinventing.
And reinventing is "more work" (TM). Top of my head, CLOS gives you
hierarchies easily, with generic functions doing the right thing
by default when called with an instance of the subclass. That's the
"standard method combination" [1], easy to define readers, writers
or full accessors... all as generic functions. A uniform data
representation for which you can someday (TM) write an interactive
object inspector.
> > I'd guess the cl-averse are still doing structs with vectors :-)
>
> I don't think I'm strongly "cl-averse", but I'm rather not familiar
> enough with it to know its benefits. Having modified some of its code
> I'm familiar with its cost and what it does, but that doesn't
> immediately translate into ideas for how I could make good use of it.
Yes, I have a lot of experience with both Lisps so it's easy to
see the difference where one trumps the other, sometimes
spectacularly. Elisp does trump CL in some every specific areas,
Edebug is easily one of them.
I'm enjoying reading this new "community spec" of CL (links below),
it's really well done.
And if anyone can learn CL in a weekend, it's you ;-)
> I don't think this has anything to do with `&rest` or not. It sounds
> like a bug in `shared-initialize` or `initialize-instance` (or
> a disagreement between the two). I suggest you try and write a short
> recipe for it and make it a bug report.
I think I already did (well maybe not report-emacs-bug, but here's
the problem again)
; SLY 1.0.43 (#<MREPL mrepl-1-1>)
CL-USER> (defclass foo () ((bar :initarg :bar) (baz :initarg :baz)))
#<STANDARD-CLASS COMMON-LISP-USER::FOO>
CL-USER> (make-instance 'foo :bar 42 :baz 42) ; presumed client code
#<FOO {10043F2073}>
CL-USER> (defclass foo () ((bar :initarg :bar)))
#<STANDARD-CLASS COMMON-LISP-USER::FOO>
CL-USER> (make-instance 'foo :bar 42 :baz 42) ; whoops
; Debugger entered on #<SB-PCL::INITARG-ERROR {1004464F83}>
CL-USER> (defvar *bazzes* (make-hash-table :weakness :key))
*BAZZES*
CL-USER> (defmethod initialize-instance :after ((x foo) &key baz
&allow-other-keys)
(setf (gethash x *bazzes*) baz))
#<STANDARD-METHOD COMMON-LISP:INITIALIZE-INSTANCE :AFTER (FOO) {10015066B3}>
CL-USER> (make-instance 'foo :bar 42 :baz 42) ; now works again
#<FOO {1001506EA3}>
CL-USER>
*** Welcome to IELM *** Type (describe-mode) or press C-h m for help.
ELISP> (defclass foo () ((bar :initarg :bar) (baz :initarg :baz)))
foo
ELISP> (make-instance 'foo :bar 42 :baz 42) ; so far so good
#<foo foo-156aec4e27b2>
ELISP> (defclass foo () ((bar :initarg :bar))) ; redef
foo
ELISP> (make-instance 'foo :bar 42 :baz 42) ; will error, good, expected
*** Eval error *** Invalid slot name: "#<foo foo-156aec62ba72>", :baz
ELISP> (cl-defmethod initialize-instance :after ((x foo) ((&key baz
&allow-other-keys) t))
(setf (gethash x *bazzes*) baz)) ^^^^ odd
specializer, but OK
initialize-instance
ELISP> (defvar bazzes (make-hash-table :weakness 'key))
bazzes
ELISP> (make-instance 'foo :bar 42 :baz 42) ;; will error unexpected
*** Eval error *** Invalid slot name: "#<foo foo-156aec6034ce>", :baz
Yes, happens in shared-initialize, but can it be fixed while
also keeping the expected error above? Doesn't seem easy.
In CLOS, the initialize-instance generic automagically becomes
"other-key" friendly if -- and only if -- one of its methods is.
From [2]:
The set of valid initialization arguments for a class is the
set of valid initialization arguments that either fill slots or
supply arguments to methods, along with the predefined
initialization argument :allow-other-keys. The default value for
:allow-other-keys is nil.
In fact, this allows you to keep the tight initarg validation
behaviour even without &allow-other-keys in there. So in CLOS, you
can
(defmethod initialize-instance :after ((x foo) &key baz)
(setf (gethash x *bazzes*) baz))
So that
(make-instance 'foo :blergh 42)
still reassuringly errors.
> >> I'm afraid that doesn't tell me how to attach context to an
> >> error object. The context I'm talking about are orthogonal to the error
> >> objects themselves: any kind of context info could be attached to any
> >> kind of error object.
> > I don't know what "context" you are talking about (I guess it
> > was in the initial email, but I couldn't grok it).
>
> In different cases we may want to add contextual info about an error,
> such as the time at which it was signaled, the backtrace, some subset of
> the backtrace (e.g. just the name of the innermost function), or the
> list of files we're in the middle of loading, or some node in
> a data structure within which we were operating when the error occurred,
> ...
>
> Usually which info to collect will depend on what it will be used for,
> so it's under the control of the outer code, and is thus orthogonal to
> the error that's signaled.
OK, I think I'm starting to see. You're looking at this from a data
representation perspective, and CL solves (presumably) the same
problem you are trying to solve from more of a behavioral perspective.
What CL implementations do for the "conveniences "I presume you're
talking about is establish restarts and keep this information in
the stack.
Say you have this generic form
(with-foo-convenience ()
(with-bar-convenience ()
(the-great-unknown)))
So if a CALL-WITH-FOO-CONVENIENCE is dynamically wrapping some error
throwing code, it will typically let-bind *FOO-DATA*, some restart
that operates (reading or writing) on that *FOO-DATA* and a
handler-bind.
Note that restarts aren't only invocable, stack-unwinding things:
they're also self-reporting and can hide themselves. This becomes
relevant as other handlers -- or the human operating the debugger --
tries to find them to fix the course of the program.
Anyway, when an error crosses the handler-bind in its upstack path,
*FOO-DATA* may be augmented with the "context" and nothing else
happens. When ultimately the human-operated debugger is reached the
restarts that are available there can are nicely listed with
formatted details as their report functions are invoked and read
from the enriched *FOO-DATA*.
For example, *FOO-DATA* could keep track of the time it observed the
LITTLE-GREEN-MAN condition C pass by. And the REPORT-TO-SETI restart's
human readable :REPORT would look something like
"Report suspicious sighting at XX:XX:XX UTC" or something.
Importantly, a HANDLER-BIND in the inner CALL-WITH-BAR-CONVENIENCE can
_also_ invoke restarts previously established by
CALL-WITH-FOO-CONVENIENCE, that are only "findable" if those
restarts' :TEST functions [3] say they are. These functions also
have access to C and lexical access to *FOO-DATA*.
In summary, I think the "context" you are thinking about can just
be good old dynamic context, i.e. let-bindings of special
variables, with the new HANDLER-BIND-powered twist that these
variables can remain private to their users _and_ be augmented
after they are initially established.
Right? Wrong? It would at least seem to alleviate your own doubts
about the context "going unused most of the time". It keeps the
"orthogonal" property and no general-purpose duck-typed "context"
needed in the base object or in a hash table or somesuch.
> > Ugh indeed. 40 years of leaky abstractions, ¯\_(ツ)_/¯
> >
> > So yes, "error" err would have that poor man's cons. Maybe
> > condition-case would only catch those? A new shiny
> > handler-case on the other hand, would catch proper richie
> > rich errors (and have the better syntax).
>
> That entails a heavy amount of churn to change old code to use that new
> form (while we'd want to support both for backward compatibility, we'd
Not sure if you understand that what I proposed _was_ in principle
100% backward compatible. So on day 0 that 'error' (or in fact
'signal) starts throwing objects and condition-case reconstructs
the cons nothing needs to change immediately.
> also want to slowly get rid of the old form to keep the overall
> complexity in check).
That's never stopped us from introducing better abstractions.
In fact there's talk right of introducing a cond* to replace
10 year old pcase, with very heavy question marks whether it is
really better. Where I think these question marks simply are not
there for handler-bind and handler-case (restarts, CLOS and very
many CL things).
And -- I'm not kidding -- i think 90-95% of this particular
trivial code churn (turning condition-case to handler-case)
is easily handled by AI things these days. See [4] for a brilliant
presentation of something I plan to use soon.
> The upside doesn't seem worth the cost for now.
It'll be harder 40 years from now.
João
[1]: https://cl-community-spec.github.io/pages/Standard-Method-Combination.html
[2]: https://cl-community-spec.github.io/pages/Declaring-the-Validity-of-Initialization-Arguments.html#Declaring-the-Validity-of-Initialization-Arguments
[3]: https://cl-community-spec.github.io/pages/restart_002dcase.html
[4]: https://www.youtube.com/watch?v=bsRnh_brggM
next prev parent reply other threads:[~2023-12-28 14:12 UTC|newest]
Thread overview: 43+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-12-21 22:30 Attaching context info to an error Stefan Monnier
2023-12-22 6:50 ` Gerd Möllmann
2023-12-22 8:37 ` Gerd Möllmann
2023-12-22 15:58 ` Stefan Monnier
2023-12-28 6:57 ` Gerd Möllmann
2023-12-22 20:56 ` Jens Schmidt
2023-12-22 22:37 ` Stefan Monnier
2023-12-23 3:02 ` João Távora
2023-12-23 3:28 ` João Távora
2023-12-26 20:12 ` Stefan Monnier
2023-12-26 20:47 ` Stefan Monnier
2023-12-26 22:43 ` João Távora
2023-12-27 6:50 ` Gerd Möllmann
2023-12-27 10:29 ` João Távora
2023-12-27 10:35 ` Gerd Möllmann
2023-12-27 17:50 ` Stefan Monnier
2023-12-27 18:08 ` João Távora
2023-12-27 18:28 ` João Távora
2023-12-27 19:08 ` Stefan Monnier
2023-12-27 19:27 ` João Távora
2023-12-27 20:27 ` Stefan Monnier
2023-12-27 23:08 ` João Távora
2023-12-28 7:05 ` Stefan Monnier
2023-12-28 14:12 ` João Távora [this message]
2023-12-28 16:03 ` Stefan Monnier
2023-12-28 17:15 ` João Távora
2023-12-28 19:22 ` Stefan Monnier
2023-12-28 23:53 ` João Távora
2023-12-29 2:54 ` Stefan Monnier
2023-12-29 3:43 ` João Távora
2023-12-29 16:54 ` Stefan Monnier
2023-12-29 17:29 ` João Távora
2023-12-29 17:39 ` João Távora
2023-12-30 4:29 ` Stefan Monnier
2023-12-30 16:45 ` João Távora
2023-12-29 17:19 ` Alan Mackenzie
2023-12-29 17:24 ` João Távora
2023-12-29 17:43 ` Alan Mackenzie
2023-12-29 17:54 ` João Távora
2023-12-29 18:08 ` Alan Mackenzie
2023-12-29 18:45 ` João Távora
2023-12-29 18:35 ` Stefan Monnier
2023-12-29 18:48 ` João Távora
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/emacs/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=CALDnm51gurztb0dr-XF+dX6dZUX83H0oz3S4kMvPL+fBe3fUEA@mail.gmail.com \
--to=joaotavora@gmail.com \
--cc=emacs-devel@gnu.org \
--cc=monnier@iro.umontreal.ca \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).