unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Attaching context info to an error
@ 2023-12-21 22:30 Stefan Monnier
  2023-12-22  6:50 ` Gerd Möllmann
                   ` (2 more replies)
  0 siblings, 3 replies; 43+ messages in thread
From: Stefan Monnier @ 2023-12-21 22:30 UTC (permalink / raw)
  To: emacs-devel

I'm playing with `handler-bind` and trying to see how we could make use
of such a functionality in Emacs.  So far I have encountered basically
two use case:

- Triggering the debugger.  I have patches which use `handler-bind` for
  that instead of `debug-on-error` in ERT, `eval-expression` and
  `--debug-init` which circumvent the usual problems linked to (ab)using
  `debug-on-error`.

- I see a potential use in Tramp where we currently use
  `signal-hook-function` instead.  I haven't looked closely enough yet
  to be sure that it's a good replacement there, tho.

- Collecting dynamic context info.

This third use case is for thing like
`macroexp--with-extended-form-stack` where we'd like to record the
`byte-compile-form-stack` that's current when the error is signaled so
we can use that info back where we actually catch that error.

Similarly when we load a file and that signals an error, we'd like to be
able to attach to the error the information that it occurred while
loading file FOO (and that could stack up if it occurred while file FOO
was loading file BAR).

The question is: where should we put this "context" info.

One possibility is to do something like the following:


    (defun load (file ...)
      (handler-bind ((error (lambda (err)
                              (signal 'error-during-load
                                      (cons file err)))))
        ...))

so an error that occurs during `load` is turned into an
`error-during-load` (and those errors contain the original error as
well as the relevant file name, the original error could be an
`error-during-load` itself, etc...).

Similarly the byte-compiler could do

    (defun byte-compile-blabla (...)
      ...
      (condition-case err
          (handler-bind ((error
                          (lambda (err)
                            (signal 'error-during-bytecomp
                                    (cons `byte-compile-form-stack` err)))))
            ...)
        (error
         ;; `err` should now be of the form
         ;; (error-during-bytecomp STACK ERROR)
         ...)))

but I don't like the idea of changing one kind of error into another.

I thought about adding the context info to an auxiliary hash table
indexed by the "error object", but that error object tends to be
decomposed into "error-name + error-data" and then recomposed fairly
liberally, so there's no guarantee that it stays `eq` to itself.

Ideally, I'd like to "append it" to the `cdr` of an error object
(i.e. the "error data"), but I can't think of a way to do it that's
reliable and doesn't introduce nasty backward compatibility issues
(and/or require changes in many places).

Any other idea how/where we could attach that info?


        Stefan




^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  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-22 20:56 ` Jens Schmidt
  2023-12-23  3:02 ` João Távora
  2 siblings, 2 replies; 43+ messages in thread
From: Gerd Möllmann @ 2023-12-22  6:50 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

Stefan Monnier <monnier@iro.umontreal.ca> writes:

> The question is: where should we put this "context" info.
>
> One possibility is to do something like the following:
>
>
>     (defun load (file ...)
>       (handler-bind ((error (lambda (err)
>                               (signal 'error-during-load
>                                       (cons file err)))))
>         ...))

I'm just guessing - so the argument ERR of the handler function is not a
`condition' object of some kind, in the CL sense? What is it?

Or can I look myself somewhere?

> ...
> Any other idea how/where we could attach that info?

I don't have a good idea either, I'm afraid.



^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  2023-12-22  6:50 ` Gerd Möllmann
@ 2023-12-22  8:37   ` Gerd Möllmann
  2023-12-22 15:58   ` Stefan Monnier
  1 sibling, 0 replies; 43+ messages in thread
From: Gerd Möllmann @ 2023-12-22  8:37 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

Gerd Möllmann <gerd.moellmann@gmail.com> writes:

> Or can I look myself somewhere?

Found the scratch/handler-bind branch now, after fetching.



^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  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
  1 sibling, 1 reply; 43+ messages in thread
From: Stefan Monnier @ 2023-12-22 15:58 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: emacs-devel

>> The question is: where should we put this "context" info.
>>
>> One possibility is to do something like the following:
>>
>>     (defun load (file ...)
>>       (handler-bind ((error (lambda (err)
>>                               (signal 'error-during-load
>>                                       (cons file err)))))
>>         ...))
>
> I'm just guessing - so the argument ERR of the handler function is not a
> `condition' object of some kind, in the CL sense? What is it?

No, it's the same kind of objects as are bound to `err` in
(condition-case err ...), i.e. a cons of (ERROR-NAME . ERROR-DATA).

I don't think it makes sense to have different kinds of objects for
`handler-bind` and for `condition-case`.


        Stefan




^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  2023-12-21 22:30 Attaching context info to an error Stefan Monnier
  2023-12-22  6:50 ` 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
  2 siblings, 1 reply; 43+ messages in thread
From: Jens Schmidt @ 2023-12-22 20:56 UTC (permalink / raw)
  To: Stefan Monnier, emacs-devel

I might lack experience here, but I give it a try, anyway ...

On 2023-12-21  23:30, Stefan Monnier wrote:

> I'm playing with `handler-bind` and trying to see how we could make use
> of such a functionality in Emacs.

`handler-bind' seems to have the main advantage over `condition-case'
that you get the full information about the original error stack, while
in a `condition-case' that information is lost, correct?

> [...]
> 
> but I don't like the idea of changing one kind of error into another.

Why not?  Wrapping errors that way is what I know.

> Any other idea how/where we could attach that info?

Property list of the error-symbol?  Not beautiful, but probably more
stable than "external" storage as in a hash table.  Probably too stable
in the sense that the property list (without further provisions) would
survive the lifetime of the error.



^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  2023-12-22 20:56 ` Jens Schmidt
@ 2023-12-22 22:37   ` Stefan Monnier
  0 siblings, 0 replies; 43+ messages in thread
From: Stefan Monnier @ 2023-12-22 22:37 UTC (permalink / raw)
  To: Jens Schmidt; +Cc: emacs-devel

>> I'm playing with `handler-bind` and trying to see how we could make use
>> of such a functionality in Emacs.
> `handler-bind' seems to have the main advantage over `condition-case'
> that you get the full information about the original error stack, while
> in a `condition-case' that information is lost, correct?

Yes, it's a kind of mix between `condition-case` and `signal-hook-function`.

>> but I don't like the idea of changing one kind of error into another.
> Why not?  Wrapping errors that way is what I know.

Because a (condition-case err (FOO) (wrong-number-of-arguments ...))
won't catch an `error-during-load` even if that error is wrapped around
a `wrong-number-of-arguments` error.

>> Any other idea how/where we could attach that info?
> Property list of the error-symbol?

That's storing the info in a global var, which is hackish: as you say it
will sometimes "survive the lifetime of the error" but it will also
sometimes live a too short life if some other error happens between the
moment the first error is signaled and that first error is caught.
[ This is likely to occur if we run the debugger between those two, for
  instance, or if an unwind form signal an error, ...  ]

FWIW, a global variable is what we use for the only such "error context
information" we currently have, which is stored in
`Vsignalling_function` (not exposed to ELisp, but visible sometimes in
*Messages*).


        Stefan




^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  2023-12-21 22:30 Attaching context info to an error Stefan Monnier
  2023-12-22  6:50 ` Gerd Möllmann
  2023-12-22 20:56 ` Jens Schmidt
@ 2023-12-23  3:02 ` João Távora
  2023-12-23  3:28   ` João Távora
  2023-12-26 20:47   ` Stefan Monnier
  2 siblings, 2 replies; 43+ messages in thread
From: João Távora @ 2023-12-23  3:02 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

On Thu, Dec 21, 2023 at 10:31 PM Stefan Monnier
<monnier@iro.umontreal.ca> wrote:

> I'm playing with `handler-bind` and trying to see how we could make use
> of such a functionality in Emacs.

*loud cheers*

> So far I have encountered basically two use case:

(...proceeds to list three use cases...)

Amongst the killer weaponry this gives us is a restart system,
finally.

This completely changes interactive development.  Whereas before
you couldn't restart from an nearby stack frame after doing
some changes to the state (say recompiling a buggy function, or
setting a global variable),  with a handler-bind-powered restart
system you finally can.

So I had to try your branch...

;; -*- lexical-binding: t -*-
(require 'cl-lib)

(defvar pm/restarts nil)

(defun pm/call-with-retry-restart (outer inner msg fn)
  (catch outer
    (let ((saved-restarts pm/restarts))
      (unwind-protect
          (while t
            (catch inner
              (handler-bind ((error (lambda (_e)
                                      (push
                                       (list 'retry msg
                                             (lambda ()
                                               (throw inner nil)))
                                       pm/restarts))))
                (throw outer (funcall fn)))))
        (setq pm/restarts saved-restarts)))))

(cl-defmacro pm/with-retry-restart ((&key (msg "Retry.")) &body body)
  `(pm/call-with-retry-restart (gensym) (gensym) ,msg (lambda () ,@body)))

(defun pm/invoke-restart (restart)
  (interactive
   (some-read-from-minibuffer-or-somesuch))
  (funcall (caadr restart)))

(defun baz (sym)
  (error "whoops, it happened again for %s" sym))

(defun bar ()
  (pm/with-retry-restart (:msg "Retry") (baz (gensym))))

(defun foo ()
  (bar))

(foo)

;; Local Variables:
;; read-symbol-shorthands: (("pm/" . "poor-mans-restarts-"))
;; End:

This very code works fine in Common Lisp (just add a WHILE
macro to it) however when I try this in your Emacs branch and
evaluate (foo) I get:

   Re-entering top level after C stack overflow

and Emacs dies immediately.

João

PS: Of course Common Lisp has all of this built-in and with
much, much better primitives that cover a wealth of use cases.
So this was just a quick-n-dirty proof of concept.



^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  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
  1 sibling, 1 reply; 43+ messages in thread
From: João Távora @ 2023-12-23  3:28 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

On Sat, Dec 23, 2023 at 3:02 AM João Távora <joaotavora@gmail.com> wrote:
> (defun pm/invoke-restart (restart)
>   (interactive
>    (some-read-from-minibuffer-or-somesuch))
>   (funcall (caadr restart)))
>
> (defun baz (sym)
>   (error "whoops, it happened again for %s" sym))
>
> (defun bar ()
>   (pm/with-retry-restart (:msg "Retry") (baz (gensym))))
>
> (defun foo ()
>   (bar))
>
> (foo)
>
> ;; Local Variables:
> ;; read-symbol-shorthands: (("pm/" . "poor-mans-restarts-"))
> ;; End:
>
> This very code works fine in Common Lisp (just add a WHILE
> macro to it)

Just to be clear what I meant by this to those unfamiliar with
Common Lisp and interactive restarts.  It means I can invoke
the restart interactively from the Common Lisp debugger that
is invoked as the last step of the non-returning error.

'baz' will be called again and again with different arguments
and always errors until I recompile it to something else that
doesn't error, which I can do while the debugger is active.

I expected to be able to do the same from the Emacs Lisp
debugger-mode/backtrace-mode *Backtrace* buffer, but I never
get one such buffer.

João



^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  2023-12-23  3:28   ` João Távora
@ 2023-12-26 20:12     ` Stefan Monnier
  0 siblings, 0 replies; 43+ messages in thread
From: Stefan Monnier @ 2023-12-26 20:12 UTC (permalink / raw)
  To: João Távora; +Cc: emacs-devel

> Just to be clear what I meant by this to those unfamiliar with
> Common Lisp and interactive restarts.  It means I can invoke
> the restart interactively from the Common Lisp debugger that
> is invoked as the last step of the non-returning error.
>
> 'baz' will be called again and again with different arguments
> and always errors until I recompile it to something else that
> doesn't error, which I can do while the debugger is active.
>
> I expected to be able to do the same from the Emacs Lisp
> debugger-mode/backtrace-mode *Backtrace* buffer, but I never
> get one such buffer.

FWIW, it works for me (with an Emacs compiled from the
`scratch/handler-bind` branch).
when I do `C-x C-e` at the end of `(foo)`, I get into the debugger, and
I do

    M-: (funcall (nth 2 (car pm/restarts))) RET

I get back into the debugger but with a different gensymd symbol.
And if I redefine `baz` to something that doesn't signal an error,

    M-: (funcall (nth 2 (car pm/restarts))) RET

then lets me return to the toplevel.


        Stefan




^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  2023-12-23  3:02 ` João Távora
  2023-12-23  3:28   ` João Távora
@ 2023-12-26 20:47   ` Stefan Monnier
  2023-12-26 22:43     ` João Távora
  1 sibling, 1 reply; 43+ messages in thread
From: Stefan Monnier @ 2023-12-26 20:47 UTC (permalink / raw)
  To: João Távora; +Cc: emacs-devel

>> I'm playing with `handler-bind` and trying to see how we could make use
>> of such a functionality in Emacs.
> *loud cheers*
[...]
> Amongst the killer weaponry this gives us is a restart system,
> finally.

I fail to see the connection, to be honest.

> (defun pm/call-with-retry-restart (outer inner msg fn)
>   (catch outer
>     (let ((saved-restarts pm/restarts))
>       (unwind-protect
>           (while t
>             (catch inner
>               (handler-bind ((error (lambda (_e)
>                                       (push
>                                        (list 'retry msg
>                                              (lambda ()
>                                                (throw inner nil)))
>                                        pm/restarts))))
>                 (throw outer (funcall fn)))))
>         (setq pm/restarts saved-restarts)))))

Rather than `(let ((saved-restarts pm/restarts))` why not `(let
((pm/restarts pm/restarts))` so you don't need the `setq` in the
`unwind-protect`?

And why `handler-bind`.  IOW, why wait until the error happens before
pushing the retry onto the list?  Why not just:

    (defun pm/call-with-retry-restart (outer inner msg fn)
      (catch outer
        (let ((pm/restarts
               (cons (list 'retry msg
                           (lambda () (throw inner nil))))))
          (while t
            (catch inner
              (throw outer (funcall fn)))))))


-- Stefan




^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  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 17:50       ` Stefan Monnier
  0 siblings, 2 replies; 43+ messages in thread
From: João Távora @ 2023-12-26 22:43 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

On Tue, Dec 26, 2023 at 8:47 PM Stefan Monnier <monnier@iro.umontreal.ca> wrote:
>
> >> I'm playing with `handler-bind` and trying to see how we could make use
> >> of such a functionality in Emacs.
> > *loud cheers*
> [...]
> > Amongst the killer weaponry this gives us is a restart system,
> > finally.
>
> I fail to see the connection, to be honest.

The restarts themselves can be made with simply try/catch,
you're right, but automatically invoking restarts with certain
parameters based on different each error object can only be done
with  handler-bind AFAIK.

Also, for restarts to become visible/invisible according to
some information in the condition themselves (the type
of error or some other cookie) we also need handler-bind to
attach that cookie to the condition object.

Typically, a restart gives you the option to try again with
something different (not just try again as I did in this
poc), like if a network connection fails, you're given
the option to try with new credentials, or a different
protocol, etc...

> And why `handler-bind`.  IOW, why wait until the error happens before
> pushing the retry onto the list?  Why not just:
>
>     (defun pm/call-with-retry-restart (outer inner msg fn)
>       (catch outer
>         (let ((pm/restarts
>                (cons (list 'retry msg
>                            (lambda () (throw inner nil))))))
>           (while t
>             (catch inner
>               (throw outer (funcall fn)))))))

Probably, this was a thinko, or I was trying to simulate visibility
of a restart like I described above, but there are much better
ways to do that.

Anyway, as Gerd suggested, I would suggest at least supporting
EIEIO in this handler-bind. This needn't necessarily clash with
our "error symbols" if the dispatching is done via generic functions
right? The usual performance setbacks of EIEIO object shouldn't
be a problem as conditions are meant to represent exceptional
situations by definition. The counterpart to handler-bind is
handler-case btw (which is more or less our condition-case).



^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  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 17:50       ` Stefan Monnier
  1 sibling, 1 reply; 43+ messages in thread
From: Gerd Möllmann @ 2023-12-27  6:50 UTC (permalink / raw)
  To: João Távora; +Cc: Stefan Monnier, emacs-devel

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

> On Tue, Dec 26, 2023 at 8:47 PM Stefan Monnier <monnier@iro.umontreal.ca> wrote:
>>
>> >> I'm playing with `handler-bind` and trying to see how we could make use
>> >> of such a functionality in Emacs.
>> > *loud cheers*
>> [...]
>> > Amongst the killer weaponry this gives us is a restart system,
>> > finally.
>>
>> I fail to see the connection, to be honest.
>
> The restarts themselves can be made with simply try/catch,
> you're right, but automatically invoking restarts with certain
> parameters based on different each error object can only be done
> with  handler-bind AFAIK.

BTW, and sorry if I'm cnfusing things here, maybe.

Do you & Stefan mean, with "restart", restarts like in CL, i.e.
restart-bind, invoke-restart, find-restart, ...? I guess not, because I
think, handler-bind handlers in CL would/could use invoke-restart. At
least that's what I have in the back of my mind. So, I guess my question
is what kind of restart is this?



^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  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
  0 siblings, 1 reply; 43+ messages in thread
From: João Távora @ 2023-12-27 10:29 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: Stefan Monnier, emacs-devel

[-- Attachment #1: Type: text/plain, Size: 864 bytes --]

On Wed, Dec 27, 2023, 06:50 Gerd Möllmann <gerd.moellmann@gmail.com> wrote:

BTW, and sorry if I'm cnfusing things here, maybe.
>
> Do you & Stefan mean, with "restart", restarts like in CL, i.e.
> restart-bind, invoke-restart, find-restart, ...?


Yes, those are the ones I mean.

I guess not, because I
> think, handler-bind handlers in CL would/could use invoke-restart. At
> least that's what I have in the back of my mind. So, I guess my question
> is what kind of restart is this?
>

The same you had in the back of your mind. That's exactly how they
cooperate normally. It was I who was confusing and misremembering things
with my poc. Restarts normally aren't implemented in terms of handler-bind
as I clumsily suggested, but handler-bind is essential to invoke then or
even modify the condition as it travels up-stack.

João

>

[-- Attachment #2: Type: text/html, Size: 1703 bytes --]

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  2023-12-27 10:29         ` João Távora
@ 2023-12-27 10:35           ` Gerd Möllmann
  0 siblings, 0 replies; 43+ messages in thread
From: Gerd Möllmann @ 2023-12-27 10:35 UTC (permalink / raw)
  To: João Távora; +Cc: Stefan Monnier, emacs-devel

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

> On Wed, Dec 27, 2023, 06:50 Gerd Möllmann <gerd.moellmann@gmail.com> wrote:
>
>  BTW, and sorry if I'm cnfusing things here, maybe.
>
>  Do you & Stefan mean, with "restart", restarts like in CL, i.e.
>  restart-bind, invoke-restart, find-restart, ...?
>
> Yes, those are the ones I mean.
>
>  I guess not, because I
>  think, handler-bind handlers in CL would/could use invoke-restart. At
>  least that's what I have in the back of my mind. So, I guess my question
>  is what kind of restart is this?
>
> The same you had in the back of your mind. That's exactly how they
> cooperate normally. It was I who was confusing and misremembering
> things with my poc. Restarts normally aren't implemented in terms of
> handler-bind as I clumsily suggested, but handler-bind is essential to
> invoke then or even modify the condition as it travels up-stack.

Ahhhh, very very good! Thanks for the clarification!



^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  2023-12-26 22:43     ` João Távora
  2023-12-27  6:50       ` Gerd Möllmann
@ 2023-12-27 17:50       ` Stefan Monnier
  2023-12-27 18:08         ` João Távora
  1 sibling, 1 reply; 43+ messages in thread
From: Stefan Monnier @ 2023-12-27 17:50 UTC (permalink / raw)
  To: João Távora; +Cc: emacs-devel

> Anyway, as Gerd suggested, I would suggest at least supporting
> EIEIO in this handler-bind.

Could you give some concrete examples of things you'd (be able to) do
with it?

Given that we start with (signal ERROR-SYMBOL ERROR-DATA) and the "other
end point" of an error is the `condition-case` variable bound to
a value of the form (ERROR-SYMBOL . ERROR-DATA), I find it a bit hard to
see how we could usefully use "objects" somewhere in-between, but maybe
some examples can help me out of the rut.


        Stefan




^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  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
  0 siblings, 2 replies; 43+ messages in thread
From: João Távora @ 2023-12-27 18:08 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

On Wed, Dec 27, 2023 at 5:50 PM Stefan Monnier <monnier@iro.umontreal.ca> wrote:
>
> > Anyway, as Gerd suggested, I would suggest at least supporting
> > EIEIO in this handler-bind.
>
> Could you give some concrete examples of things you'd (be able to) do
> with it?

Just look at anyCL program that uses conditions, there are
very many of them.  One of them is Snooze [1] which uses them
to represent HTTP error codes.

First, we'd have to be able to `signal` such objects.  And then,
CLOS is good :-) you get access to lots of existing useful protocols.
initialize-instance, print-object, etc for free.

> Given that we start with (signal ERROR-SYMBOL ERROR-DATA) and the "other
> end point" of an error is the `condition-case` variable bound to
> a value of the form (ERROR-SYMBOL . ERROR-DATA), I find it a bit hard to
> see how we could usefully use "objects" somewhere in-between, but maybe
> some examples can help me out of the rut.

From a CL perspective, and even other languages, using our
"error symbol with some 'error property" representation of
errors is almost self-evidently inferior to just making it
a first class object.

  (define-condition my/condition (error) docstring (slots...))

Which is a much more normal way to define a hierarchy of
exceptions like you find in C++, Python, and probably many
others.

Yes, I know there is `define-error` for a while, but we'd still
be reinventing all those protocols in subtly idiosyncratic and
buggy forms ad-hoc in Elisp.  Which admittedly, is what
we've been doing all along, but why stray gratuitously when
given a (presumably cheap) chance to rejoin?

João

[1]: https://github.com/joaotavora/snooze



^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  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
  1 sibling, 0 replies; 43+ messages in thread
From: João Távora @ 2023-12-27 18:28 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

On Wed, Dec 27, 2023 at 6:08 PM João Távora <joaotavora@gmail.com> wrote:

> > Could you give some concrete examples of things you'd (be able to) do
> > with it?

> Just look at anyCL program that uses conditions, there are
> very many of them.  One of them is Snooze [1] which uses them
> to represent HTTP error codes.

To elaborate a little more, Snooze is (I think) a good example
of how 'handler-bind' and 'restart-case' can cooperate to
allow different modes of operation for a HTTP server (or
HTTP REST router, which is what Snooze really is).  You
may want to catch or not catch error conditions, catch or
not catch HTTP conditions, debug these conditions in your
Lisp (or via Emacs with SLY/SLIME), explain these conditions
to the HTTP client verbosely (in developement environments)
, succinctly (in production environments), muffle them,
log them, etc.

The file

  https://github.com/joaotavora/snooze/blob/master/common.lisp

contains the meat and bones of this logic, and

  https://github.com/joaotavora/snooze/blob/master/api.lisp

contains a reasonably well docstring'ed description of the
interface to these facilities.  See the docstrings of
*catch-http-conditions* and *catch-errors*.

Restarts like 'explain-verbosely' and 'explain-succinctly'
are shown to the user when the debugger is popped.  During
development the user can thus interactively control what
gets sent to the client (usually a web-browser in another
window).  Unattended operation of such a server is achieved by
automatically invoking these restarts from handler-bind
handlers.

João



^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  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
  1 sibling, 1 reply; 43+ messages in thread
From: Stefan Monnier @ 2023-12-27 19:08 UTC (permalink / raw)
  To: João Távora; +Cc: emacs-devel

>> > Anyway, as Gerd suggested, I would suggest at least supporting
>> > EIEIO in this handler-bind.
>> Could you give some concrete examples of things you'd (be able to) do
>> with it?
> Just look at anyCL program that uses conditions, there are
> very many of them.  One of them is Snooze [1] which uses them
> to represent HTTP error codes.

But these use error objects throughout.
My question was about "at least supporting EIEIO in this handler-bind".

> First, we'd have to be able to `signal` such objects.  And then,
> CLOS is good :-) you get access to lots of existing useful protocols.
> initialize-instance, print-object, etc for free.

[ Not clear what `initialize-instance` would be used for.  I also don't
  think `print-object` would be super useful, but yes a generic
  function to compute the error message would be a natural use, tho
  currently we haven't even bothered to allow the `error-message` to be
  a function, so the non-object representation of errors doesn't seem
  to be what's holding us.  ]

More importantly, that doesn't tell me what new things we could do,
most importantly how to attach context info :-)

> From a CL perspective, and even other languages, using our
> "error symbol with some 'error property" representation of
> errors is almost self-evidently inferior to just making it
> a first class object.

You don't need to convince me of that, indeed.  But that's not what we
have right now, and it's not trivial to retro-fit it cleanly in the
current system.  Luckily, AFAICT it's orthogonal to `handler-bind`.


        Stefan




^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  2023-12-27 19:08           ` Stefan Monnier
@ 2023-12-27 19:27             ` João Távora
  2023-12-27 20:27               ` Stefan Monnier
  0 siblings, 1 reply; 43+ messages in thread
From: João Távora @ 2023-12-27 19:27 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

On Wed, Dec 27, 2023 at 7:08 PM Stefan Monnier <monnier@iro.umontreal.ca> wrote:

> > Just look at anyCL program that uses conditions, there are
> > very many of them.  One of them is Snooze [1] which uses them
> > to represent HTTP error codes.
>
> But these use error objects throughout.
> My question was about "at least supporting EIEIO in this handler-bind".
>
> > First, we'd have to be able to `signal` such objects.  And then,
> > CLOS is good :-) you get access to lots of existing useful protocols.
> > initialize-instance, print-object, etc for free.
>
> [ 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].

Though, I've noted before how our initialize-instance protocol
is inferior: It takes slots as its second argument, whereas is
should take initargs instead.  This makes changing the
representation without changing the interface hard, but that
can be worked around with the slot-missing protocol.

> I also don't
>   think `print-object` would be super useful,

It's useful when printing the human-readable error message isn't
appropriate say, because it takes multiple lines.

> More importantly, that doesn't tell me what new things we could do,
> most importantly how to attach context info :-)

You can modify a condition object like you can any other
object.

> > From a CL perspective, and even other languages, using our
> > "error symbol with some 'error property" representation of
> > errors is almost self-evidently inferior to just making it
> > a first class object.
>
> You don't need to convince me of that, indeed.  But that's not what we
> have right now, and it's not trivial to retro-fit it cleanly in the
> current system.  Luckily, AFAICT it's orthogonal to `handler-bind`.

AFAICT CL:HANDLER-BIND is aware of inheritance [3], i.e. the
things you put in "type" of each handler binding in CL:HANDLER-BIND
are interpreted as designators of subclasses of CL:CONDITION and
dispatch similarly to generics.  That shouldn't be too hard
to fit if we say `cl-condition` is the base class and
`cl-error` and `cl-warning` are its children, etc.

João

[1]: https://cl-community-spec.github.io/pages/signal.html
[2]: https://cl-community-spec.github.io/pages/error.html
[3]: https://cl-community-spec.github.io/pages/handler_002dbind.html



^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  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
  0 siblings, 1 reply; 43+ messages in thread
From: Stefan Monnier @ 2023-12-27 20:27 UTC (permalink / raw)
  To: João Távora; +Cc: emacs-devel

>> [ 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.

> Though, I've noted before how our initialize-instance protocol
> is inferior: It takes slots as its second argument, whereas is
> should take initargs instead.  This makes changing the
> representation without changing the interface hard, but that
> can be worked around with the slot-missing protocol.

[ While Eric called it "slots" it holds the plist of initargs, just
  like the CLOS one.  The only difference I know is that CLOS passes it as
  an `&rest` arg.  You also told me that CLOS includes the "default" init
  args taken from the `defclass`, but I'm not familiar with
  those details (neither for CLOS nor for EIEIO).  ]

>> I also don't think `print-object` would be super useful,
> It's useful when printing the human-readable error message isn't
> appropriate say, because it takes multiple lines.

I'm not sure the difference qualifies as "super useful".
I don't mean to say we wouldn't be happy to be able to define ad-hoc
methods for `cl-print-object` of error objects, but it's much too minor
to motivate a change of representation.

>> More importantly, that doesn't tell me what new things we could do,
>> most importantly how to attach context info :-)
> You can modify a condition object like you can any other
> object.

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 guess the only way this would help is if we had the foresight, when
designing the root class for error objects, to include a `context` slot
(even though it might go unused most of the time).
Is that what you had in mind?

>> > From a CL perspective, and even other languages, using our
>> > "error symbol with some 'error property" representation of
>> > errors is almost self-evidently inferior to just making it
>> > a first class object.
>>
>> You don't need to convince me of that, indeed.  But that's not what we
>> have right now, and it's not trivial to retro-fit it cleanly in the
>> current system.  Luckily, AFAICT it's orthogonal to `handler-bind`.
>
> AFAICT CL:HANDLER-BIND is aware of inheritance [3], i.e. the
> things you put in "type" of each handler binding in CL:HANDLER-BIND
> are interpreted as designators of subclasses of CL:CONDITION and
> dispatch similarly to generics.  That shouldn't be too hard
> to fit if we say `cl-condition` is the base class and
> `cl-error` and `cl-warning` are its children, etc.

What I mean is that if we start using something else than cons cells to
represent error objects, loads of code will break because of things like:

    (condition-case err
        ...
      (error ... (signal (car err) (cdr err))))


-- Stefan




^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  2023-12-27 20:27               ` Stefan Monnier
@ 2023-12-27 23:08                 ` João Távora
  2023-12-28  7:05                   ` Stefan Monnier
  0 siblings, 1 reply; 43+ messages in thread
From: João Távora @ 2023-12-27 23:08 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

On Wed, Dec 27, 2023 at 8:27 PM 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, it's just
more work.  I'd guess the cl-averse are still doing structs with vectors :-)

>
> > Though, I've noted before how our initialize-instance protocol
> > is inferior: It takes slots as its second argument, whereas is
> > should take initargs instead.  This makes changing the
> > representation without changing the interface hard, but that
> > can be worked around with the slot-missing protocol.
>
> [ While Eric called it "slots" it holds the plist of initargs, just
>   like the CLOS one.  The only difference I know is that CLOS passes it as
>   an `&rest` arg.

Yes, but it's a big one.

Out versionwill go looking for slots for that each initarg and if the
slot doesn't exist it barfs, whereas in Common Lisp you can make it
&accept-other-keys.

I think I also wrote about this off list.  In CL, make-instance can
be given arbitrary keyword args and they will show up in the
initialize-instance.  They may or may not line up with a slot's
initarg.  This enables us to change the data representation while
keeping the interface to the class.  See the current master's
lisp/jsonrpc.el for an example where I had to use slot-missing to
get around this.

> You also told me that CLOS includes the "default" init
>   args taken from the `defclass`, but I'm not familiar with
>   those details (neither for CLOS nor for EIEIO).  ]

Also that.

> >> I also don't think `print-object` would be super useful,
> > It's useful when printing the human-readable error message isn't
> > appropriate say, because it takes multiple lines.
>
> I'm not sure the difference qualifies as "super useful".
> I don't mean to say we wouldn't be happy to be able to define ad-hoc
> methods for `cl-print-object` of error objects, but it's much too minor
> to motivate a change of representation.

You don't even have to define anything.  The cl-print-object of
any EIEIO object is acceptable as a start.  A more advanced
version will do stuff with the objects 'format-control' and
'format-arguments', respecting print-escape and print-readably.

> >> More importantly, that doesn't tell me what new things we could do,
> >> most importantly how to attach context info :-)
> > You can modify a condition object like you can any other
> > object.
>
> 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).  A setf-able
condition-context generic sounds fair to me tho.  Maybe start
with an external representation in a weak-keyed hash table,
then move it to a slot in the top of the hierarchy?

> I guess the only way this would help is if we had the foresight, when
> designing the root class for error objects, to include a `context` slot
> (even though it might go unused most of the time).
> Is that what you had in mind?

Ah, yes!  Read above, great minds... as usual ;-)

> What I mean is that if we start using something else than cons cells to
> represent error objects, loads of code will break because of things like:
>
>     (condition-case err
>         ...
>       (error ... (signal (car err) (cdr err))))

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).

Or maybe do some other syntax trick?

(condition-case err
   ...
  (error ((e)) ... ); 'err' is that strange cons, 'e' is a proper condition
  (warning ((w)) ... ); ditto for 'err' and w.
 )

I'm sure you can come up with better tho :-)

João



^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  2023-12-22 15:58   ` Stefan Monnier
@ 2023-12-28  6:57     ` Gerd Möllmann
  0 siblings, 0 replies; 43+ messages in thread
From: Gerd Möllmann @ 2023-12-28  6:57 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

Stefan Monnier <monnier@iro.umontreal.ca> writes:

>>> The question is: where should we put this "context" info.
>>>
>>> One possibility is to do something like the following:
>>>
>>>     (defun load (file ...)
>>>       (handler-bind ((error (lambda (err)
>>>                               (signal 'error-during-load
>>>                                       (cons file err)))))
>>>         ...))
>>
>> I'm just guessing - so the argument ERR of the handler function is not a
>> `condition' object of some kind, in the CL sense? What is it?
>
> No, it's the same kind of objects as are bound to `err` in
> (condition-case err ...), i.e. a cons of (ERROR-NAME . ERROR-DATA).
>
> I don't think it makes sense to have different kinds of objects for
> `handler-bind` and for `condition-case`.

WRT to "collecting dynamic context info". I don't know if the following
is useful, it depends on what the context info is, but maybe it can
serve as an inspiration?

I was reading CMUCL code a bit today. CMUCL (and SBCL) do something 
like the below for handler-bind (and analogously for restart-bind):

  (defvar *handler-clusters* nil)

  (defmacro handler-bind (bindings &body forms)
    "(HANDLER-BIND ( {(type handler)}* )  body)
     Executes body in a dynamic context where the given handler bindings are
     in effect.  Each handler must take the condition being signalled as an
     argument.  The bindings are searched first to last in the event of a
     signalled condition."
    (unless (every #'(lambda (x) (and (listp x) (= (length x) 2))) bindings)
      (simple-program-error (intl:gettext "Ill-formed handler bindings.")))
    `(let ((*handler-clusters*
            (cons (list ,@(mapcar #'(lambda (x) `(cons ',(car x) ,(cadr x)))
                                  bindings))
                  *handler-clusters*)))
       (multiple-value-prog1
        (progn ,@forms)
        ;; Wait for any float exceptions
        #+x87 (float-wait))))

Each handler-bind is associated with a binding of *handler-clusters*,
which contains two things: the handlers of the handler-bind, and a link
to the next such binding up the stack. (This chain is used to find
handlers etc.).

Maybe one could use such a construct in Emacs to additionally associate
context with dynamic occurrances of handler-bind?



^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  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
  0 siblings, 1 reply; 43+ messages in thread
From: Stefan Monnier @ 2023-12-28  7:05 UTC (permalink / raw)
  To: João Távora; +Cc: emacs-devel

>> >> [ 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.

> 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.

>> [ While Eric called it "slots" it holds the plist of initargs, just
>>   like the CLOS one.  The only difference I know is that CLOS passes it as
>>   an `&rest` arg.
>
> Yes, but it's a big one.

I completely fail to see why.

> Our version will go looking for slots for that each initarg and if the
> slot doesn't exist it barfs, whereas in Common Lisp you can make it
> &accept-other-keys.

You lost me here.  How does that relate to `&rest` vs `not &rest`?

> I think I also wrote about this off list.  In CL, make-instance can
> be given arbitrary keyword args and they will show up in the
> initialize-instance.  They may or may not line up with a slot's
> initarg.

AFAIK the same thing holds for EIEIO's `initialize-instance`.

> This enables us to change the data representation while
> keeping the interface to the class.  See the current master's
> lisp/jsonrpc.el for an example where I had to use slot-missing to
> get around this.

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'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 datastructure 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.

>> What I mean is that if we start using something else than cons cells to
>> represent error objects, loads of code will break because of things like:
>>
>>     (condition-case err
>>         ...
>>       (error ... (signal (car err) (cdr err))))
>
> 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
also want to slowly get rid of the old form to keep the overall
complexity in check).

The upside doesn't seem worth the cost for now.


        Stefan




^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  2023-12-28  7:05                   ` Stefan Monnier
@ 2023-12-28 14:12                     ` João Távora
  2023-12-28 16:03                       ` Stefan Monnier
  0 siblings, 1 reply; 43+ messages in thread
From: João Távora @ 2023-12-28 14:12 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

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



^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  2023-12-28 14:12                     ` João Távora
@ 2023-12-28 16:03                       ` Stefan Monnier
  2023-12-28 17:15                         ` João Távora
  0 siblings, 1 reply; 43+ messages in thread
From: Stefan Monnier @ 2023-12-28 16:03 UTC (permalink / raw)
  To: João Távora; +Cc: emacs-devel

> It's not just initialize-instance, of course, it's CLOS or
> an equivalent.

You wrote:

    [...] And then, CLOS is good :-) you get access to lots of existing
    useful protocols.  initialize-instance, print-object, etc for free.

Which implies that `initialize-instance` is a useful protocol.
I'd just be happy to see a concrete example of something useful you can
do with it that's easier than what we do with "old style Lisp".

> 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.

You do realize that I wrote `cl-generic.el`, made changes to
`cl-defstruct` to better integrate with EIEIO, ...

> That's the "standard method combination" [1],

... and this as well, right?

> *** 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

Redefinition of a class is definitely not currently supported (with no
plan to support it in the foreseeable future).

But in the `jsonrpc.el` there is no redefinition, so it seems to be
another problem.  Can you open a bug with a small recipe that
reproduces the problem that you encountered while writing `jsonrpc.el`?

> 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?

That describes where the context is placed until the error is signaled.
The question is how/where to preserve (part of) this context information
when some code wants to use it *after* unwinding the stack.

E.g. in `bytecomp.el` on the handler-bind branch I solved it as follows:

	      (let ((form-stack nil))
		(condition-case error-info
		    (handler-bind
		        ((error (lambda (_err)
		                  (setq form-stack byte-compile-form-stack))))
		      (funcall body-fn))
		  (error (let ((byte-compile-form-stack form-stack))
		           (byte-compile-report-error error-info))))))))

where `byte-compile-form-stack` is basically your *FOO-DATA*, and
I preserve/propagate this info by stashing it in the
`form-stack` variable.

Maybe we could have gotten away with calling `byte-compile-report-error`
directly from the `handler-bind` handler, but that could be affected by
all kinds of dynamic let-bindings and whatnot.

Another example is in the C code: when we call `signal`, the C code
walks the backtrace to find the name of the inner function, stores it in
the global `Vsignaling_function` and then uses it in
`cmd_error(_internal)` (i.e. after unwinding the stack) to include it in
the error message.  The us of a global var here is not just ugly but
it's also wrong because other ELisp code can be executed between the
moment we store the info in the var and the moment we use it, so it can
be polluted by the info from some other error.
[ And yes, we could rewrite that code to do something like what I did
  in `bytecomp.el` above.  ]

>> The upside doesn't seem worth the cost for now.
> It'll be harder 40 years from now.

I guess we'll see :-)


        Stefan




^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  2023-12-28 16:03                       ` Stefan Monnier
@ 2023-12-28 17:15                         ` João Távora
  2023-12-28 19:22                           ` Stefan Monnier
  0 siblings, 1 reply; 43+ messages in thread
From: João Távora @ 2023-12-28 17:15 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

On Thu, Dec 28, 2023 at 4:03 PM Stefan Monnier <monnier@iro.umontreal.ca> wrote:
>
> > It's not just initialize-instance, of course, it's CLOS or
> > an equivalent.
>
> You wrote:
>
>     [...] And then, CLOS is good :-) you get access to lots of existing
>     useful protocols.  initialize-instance, print-object, etc for free.
>
> Which implies that `initialize-instance` is a useful protocol.
> I'd just be happy to see a concrete example of something useful you can
> do with it that's easier than what we do with "old style Lisp".

Just see eglot.el and jsonrpc.el for examples.  It's a constructor
in the C++ style.  Meaning the client of the library, when making
an object, won't get just a bunch of data.  They will either
get a fully functioning object _or_ an error.  You can't do that
in plain Elisp without "-init" custom constructors, just as
you can't do that in C without foo = (Foo*)malloc() + init_foo(foo).

Furthermore, just like in typical OO, derivation of such classes
are guaranteed to have the base class parts valid and constructed
in their `intialize :after` this is the bread and butter of CLOS,
and it's awkward and error-prone to program without it.

> > 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.
>
> You do realize that I wrote `cl-generic.el`, made changes to
> `cl-defstruct` to better integrate with EIEIO, ...
>
> > That's the "standard method combination" [1],
>
> ... and this as well, right?

Right, it's because I _know_ that you wrote these things that
I find it odd you do now see how useful they are.  Maybe you
just did this as good samaritan work?  In any case, I am
extremely indebted.  It makes Eglot and other complex extensions
much easier to reason about.

> > *** 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
>
> Redefinition of a class is definitely not currently supported (with no
> plan to support it in the foreseeable future).

In this case, it was a new version of jsonrpc.el that has to
be backward compatible to clients programming against the older
version.  CLOS class redefinition is big business indeed,
but just removing a slot here in the same image worked nicely
here for demonstration purposes: it still errored as reassuringly
as it would have if in another Emacs session.

> But in the `jsonrpc.el` there is no redefinition, so it seems to be
> another problem.  Can you open a bug with a small recipe that
> reproduces the problem that you encountered while writing `jsonrpc.el`?

I was just making jsonrpc.el backward compatible to, say, older
eglot.el's.

> > 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?
>
> That describes where the context is placed until the error is signaled.

No.  _After_ the error is signalled, and before the error is _handled_.

> The question is how/where to preserve (part of) this context information
> when some code wants to use it *after* unwinding the stack.

If the stack is unwound all goes to heck indeed.  I think most problems
can (and should) be done precisely without unwinding the stack.  The
stack has all the good stuff.  Why unwind it before you need to?

> E.g. in `bytecomp.el` on the handler-bind branch I solved it as follows:
>
>               (let ((form-stack nil))
>                 (condition-case error-info
>                     (handler-bind
>                         ((error (lambda (_err)
>                                   (setq form-stack byte-compile-form-stack))))
>                       (funcall body-fn))
>                   (error (let ((byte-compile-form-stack form-stack))
>                            (byte-compile-report-error error-info))))))))
>
> where `byte-compile-form-stack` is basically your *FOO-DATA*, and
> I preserve/propagate this info by stashing it in the
> `form-stack` variable.

Why the condition-case?  Why not report it from the handler-bind and
let the error go about its business.  Or maybe don't let the error
go about its business and invoke a restart instead.

> Maybe we could have gotten away with calling `byte-compile-report-error`
> directly from the `handler-bind` handler, but that could be affected by
> all kinds of dynamic let-bindings and whatnot.

Exactly my idea above.

Are these let-bindings "legal"?  If they aren't, you can do a
WITH-STANDARD-BYTE-COMPILER-BINDINGS macro and report immediately
or you can establish a restart instead.  The restart transfers
control (and information, of course) to a point up-stack where
presumably sane bindings are in effect...

But the two options are very different.  I think the second
is better.  In general you want the error to travel further up
the stack where it can be analyzed by more clients, who add
more restarts.  You can have a much more educated pick much
in some outer part.

Then, if some *DEBUG-ON-BYTE-COMPILER-ERROR* was true, you'd get
the debugger again, with lots of interactive restarts, notably
retrying the compilation, reporting the error and aborting,
even interactive restart.  If it were false, one of those restarts
would be invoked automatically (I believe "report-and-abort")
and you'd have what you have now on your branch.

As long you invoke restarts from HANDLE-BIND clauses, you
know you have the full stack there, and this allows to have
higher level wrapping "monitoring" code that finds certain
standard restarts.  CL:ABORT the function, for example, calls
the most recently established restart named "ABORT".  See [1]
for more standard names.

> Another example is in the C code: when we call `signal`, the C code
> walks the backtrace to find the name of the inner function, stores it in
> the global `Vsignaling_function` and then uses it in
> `cmd_error(_internal)` (i.e. after unwinding the stack) to include it in
> the error message.  The us of a global var here is not just ugly but
> it's also wrong because other ELisp code can be executed between the
> moment we store the info in the var and the moment we use it, so it can
> be polluted by the info from some other error.
> [ And yes, we could rewrite that code to do something like what I did
>   in `bytecomp.el` above.  ]

Then that's what we should do :-)  Whatever this "include
in the error message" entails should be done while the Lisp
stack is still alive.  Immediately after, some Elisp version
of CL:ABORT should be called, I presume.

João

PS: Sorry for the CL::SHOUTING by the way, it's easier than
tiny quotes sometimes.

[1] https://cl-community-spec.github.io/pages/f_abort.html



^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  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
  0 siblings, 1 reply; 43+ messages in thread
From: Stefan Monnier @ 2023-12-28 19:22 UTC (permalink / raw)
  To: João Távora; +Cc: emacs-devel

> Just see eglot.el and jsonrpc.el for examples.  It's a constructor
> in the C++ style.  Meaning the client of the library, when making
> an object, won't get just a bunch of data.  They will either
> get a fully functioning object _or_ an error.  You can't do that
> in plain Elisp without "-init" custom constructors, just as
> you can't do that in C without foo = (Foo*)malloc() + init_foo(foo).

Not sure where using a "smart constructor" function (what you call
"-init" custom constructor) is less work than defining
a `initialize-instance` method.

> Furthermore, just like in typical OO, derivation of such classes
> are guaranteed to have the base class parts valid and constructed
> in their `intialize :after` this is the bread and butter of CLOS,
> and it's awkward and error-prone to program without it.

Ah... I think I'm beginning to see why I don't see the benefit: in my
world, objects are basically immutable, so an object that's partly
initialized (as in your "have the base class parts valid") is just
already very weird :-)

> Right, it's because I _know_ that you wrote these things that I find
> it odd you do now see how useful they are.

There's a difference between knowing how to build a car and being
a good driver, yes.

I can write a Prolog interpreter but my Prolog programming skills suck.
Similarly, I'm more comfortable using Haskell-style type classes (or
the closely related C++ templates) than "classic OOP" with subtyping.

>> Redefinition of a class is definitely not currently supported (with no
>> plan to support it in the foreseeable future).
> In this case, it was a new version of jsonrpc.el that has to
> be backward compatible to clients programming against the older
> version.

Then your bug report would be a feature request, I guess.
E.g. include a small patch to `jsonrpc.el` showing the code
you would *like* to use.

> CLOS class redefinition is big business indeed, but just removing
> a slot here in the same image worked nicely here for demonstration
> purposes: it still errored as reassuringly as it would have if in
> another Emacs session.

Yes, I'm aware of this.
But ELisp is a fairly limited "programming system" (as opposed to
programming language", see doi:10.1145/2384592.2384611).

I'm a PL guy and one from the functional programming side (i.e. averse
to mutation) to boot, so redefining the classes of existing objects
makes my head hurt.


        Stefan




^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  2023-12-28 19:22                           ` Stefan Monnier
@ 2023-12-28 23:53                             ` João Távora
  2023-12-29  2:54                               ` Stefan Monnier
  0 siblings, 1 reply; 43+ messages in thread
From: João Távora @ 2023-12-28 23:53 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

On Thu, Dec 28, 2023 at 7:22 PM Stefan Monnier <monnier@iro.umontreal.ca> wrote:
>
> > Just see eglot.el and jsonrpc.el for examples.  It's a constructor
> > in the C++ style.  Meaning the client of the library, when making
> > an object, won't get just a bunch of data.  They will either
> > get a fully functioning object _or_ an error.  You can't do that
> > in plain Elisp without "-init" custom constructors, just as
> > you can't do that in C without foo = (Foo*)malloc() + init_foo(foo).
>
> Not sure where using a "smart constructor" function (what you call
> "-init" custom constructor) is less work than defining
> a `initialize-instance` method.

That's right!  It isn't, it's more work, and more dangerous because
you will forget when to call it.

> > Furthermore, just like in typical OO, derivation of such classes
> > are guaranteed to have the base class parts valid and constructed
> > in their `intialize :after` this is the bread and butter of CLOS,
> > and it's awkward and error-prone to program without it.
>
> Ah... I think I'm beginning to see why I don't see the benefit: in my
> world, objects are basically immutable, so an object that's partly
> initialized (as in your "have the base class parts valid") is just
> already very weird :-)

It's not weird.  If D derived from B then the object that is passed
to the derived mode's contructor is already a fully functioning B.
This is exactly as in C++ where at least that part is bullet proof.
Not so in C and plain Elisp where allocation is one thing and
initialization another thing.

> > Right, it's because I _know_ that you wrote these things that I find
> > it odd you do now see how useful they are.
>
> There's a difference between knowing how to build a car and being
> a good driver, yes.
>
> I can write a Prolog interpreter but my Prolog programming skills suck.
> Similarly, I'm more comfortable using Haskell-style type classes (or
> the closely related C++ templates) than "classic OOP" with subtyping.

Many modern C++ template tricks are done using inheritance,
std::tuple comes to mind.  "Classic OOP" is about runtime
polymophism/virtual dispatch and yes it's seen better days
in the systems programming world, because the indirection
usually trashes caches and double dispatch is silly hard.

But for sporadic exceptions/conditions with objects generic functions
that to multiple dispatch beautifully?  I'd be hard pressed to
see better than what CL has.

> >> Redefinition of a class is definitely not currently supported (with no
> >> plan to support it in the foreseeable future).
> > In this case, it was a new version of jsonrpc.el that has to
> > be backward compatible to clients programming against the older
> > version.
>
> Then your bug report would be a feature request, I guess.

It's a bug inasmuch as EIEIO purports to be a port of CLOS,
where it's easy to do.

I don't need any class redefinition support.  The recipes
I gave earlier would be easy to replicate even if both CL
didn't support such trivial things.

> E.g. include a small patch to `jsonrpc.el` showing the code
> you would *like* to use.

I'd just like to remove the horrible slot-missing hack, it's
clearly marked in jsonrpc.el, just search for "Yuck!".

> > CLOS class redefinition is big business indeed, but just removing
> > a slot here in the same image worked nicely here for demonstration
> > purposes: it still errored as reassuringly as it would have if in
> > another Emacs session.
>
> Yes, I'm aware of this.
> But ELisp is a fairly limited "programming system" (as opposed to
> programming language", see doi:10.1145/2384592.2384611).
>
> I'm a PL guy and one from the functional programming side (i.e. averse
> to mutation) to boot, so redefining the classes of existing objects
> makes my head hurt.

Mine too, in fact in many years of CL I've not seen a single
case of useful CHANGE-CLASS.  But Emacs and Lisp machines are
designed as object landscapes mutating all the way.

Anyway, this is all besides the point: that protocol has nothing
to do with the current limitations of EIEIO's initialize-instance,
and even those limitations are quite small in comparison to the
benefits.

I see you've snipped all the rest of the "where to keep
the context" discussion.  Does that mean that my answer of
"stack _is_ the context, unwind only when absolutely done with it"
is satisfactory?  Did you understand how a standard named
restart for reporting byte compiler errors could be invoked
from "up above"?

What object exactly goes into the handler bind handlers?  Is
it the funny cons?  Can we at least get some generic functions
to hide that representation so that we can change it later on?

João



^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  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
  0 siblings, 1 reply; 43+ messages in thread
From: Stefan Monnier @ 2023-12-29  2:54 UTC (permalink / raw)
  To: João Távora; +Cc: emacs-devel

> What object exactly goes into the handler bind handlers?

The same (even `eq`) as the one passed to the `condition-case` handler.

> Is it the funny cons?

Yup.  That's ELisp's current notion of an "error object".

> Can we at least get some generic functions to hide that representation
> so that we can change it later on?

Patch very welcome, yes (and this is orthogonal to `handler-bind`).


        Stefan




^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  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:19                                   ` Alan Mackenzie
  0 siblings, 2 replies; 43+ messages in thread
From: João Távora @ 2023-12-29  3:43 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

On Fri, Dec 29, 2023 at 2:55 AM Stefan Monnier <monnier@iro.umontreal.ca> wrote:
>
> > What object exactly goes into the handler bind handlers?
>
> The same (even `eq`) as the one passed to the `condition-case` handler.

Would it matter if it weren't?

> > Is it the funny cons?
>
> Yup.  That's ELisp's current notion of an "error object".
>
> > Can we at least get some generic functions to hide that representation
> > so that we can change it later on?
>
> Patch very welcome, yes (and this is orthogonal to `handler-bind`).

What operations are typically requested from it?

- Printing?  a cl-print-object would be sufficient, I think,
  along with a (orthogonal) directive to 'format' for using
  cl-prin1 (why doesn't cl-princ exist?)

- Getting the type for resignalling purposes?  Unfortunately,
  this returns 'cons'

     (condition-case err (error "bla") (error (type-of err)))

  Can it be made to return error?

- Can we import a typep (similar to cl-typep) that understands
  basic types EIEIO and also error hierarchy?

- simple-condition-format-control and
  simple-condition-format-arguments?

- change manual to explain that resignalling is discouraged now
  that handler-bind exists, but if resignalling _is_ done, then
  definitely avoid car and cdr and some form of

    (signal (error-symbol err) (error-data err)

  introducing these new functions?

- go through the hierarchy and add <error-name>-<data-slot>
  accessors? Or do we already consider these details of "data" to
  to be hidden?  If so, I think surely some violations exist,
  hopefully not many.

  They shouldn't be terribly hard to find.  From some greps I
  estimate there less than 700 condition-case that actually use
  the variable and the majority seems to be resignalling or
  printing.  The former will probably be fixed by
  handler-bind anyway, and printing is a generic operation
  already.  I checked about 20 occurances randomly, and they
  all fell into this basket.  This also tells me that switching
  to proper objects isn't that hard.

João



^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  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:19                                   ` Alan Mackenzie
  1 sibling, 1 reply; 43+ messages in thread
From: Stefan Monnier @ 2023-12-29 16:54 UTC (permalink / raw)
  To: João Távora; +Cc: emacs-devel

>> > What object exactly goes into the handler bind handlers?
>> The same (even `eq`) as the one passed to the `condition-case` handler.
> Would it matter if it weren't?

I think we want to preserve the identity of an error as it
passes through its various handlers, yes.
That makes it possible to use `eq` hash tables and to mutate them.

> - Printing?  a cl-print-object would be sufficient, I think,
>   along with a (orthogonal) directive to 'format' for using
>   cl-prin1 (why doesn't cl-princ exist?)

We cannot reliably distinguish an error object from random other cons
cells, so things like `cl-print-object` are simply not an option: the
caller needs to tell us that this is an error object.
We could have a `print-error-object`, OTOH.

> - Getting the type for resignalling purposes?  Unfortunately,
>   this returns 'cons'
>
>      (condition-case err (error "bla") (error (type-of err)))
>
>   Can it be made to return error?

If it's only for resignaling purposes, why not have
`error-resignal` instead which takes the error object itself?

> - Can we import a typep (similar to cl-typep) that understands
>   basic types EIEIO and also error hierarchy?

Nope for the reason mentioned above.
But we could have a `error-typep`.  AFAIK this kind of operation is very
rarely used in ELisp (it's only used internally in the C code which
looks for the appropriate handler).

> - simple-condition-format-control and
>   simple-condition-format-arguments?

I can't think of any ELisp code I've encountered that does something
related, so I think it's a YAGNI.

BTW, the best way to find what we need is probably to change the
`signal_or_quit` code which does the `Fcons (error_symbol, data)` and
make it build something else than a cons instead.

Then go and fix all the things that break, introducing new `error-FOO`
abstractions along the way.

> - change manual to explain that resignalling is discouraged now
>   that handler-bind exists, but if resignalling _is_ done, then
>   definitely avoid car and cdr and some form of
>
>     (signal (error-symbol err) (error-data err)
>
>   introducing these new functions?

We first want to help people make their existing code compatible with
other representations of error objects which minimal changes.
Changing the code to use `error-resignal` is trivial whereas making it
use `handler-bind` can be difficult since the semantics is subtly
different and it's not always easy to judge whether the difference
matters in this particular case.

> - go through the hierarchy and add <error-name>-<data-slot>
>   accessors?

I don't think we want to do that systematically, but yes (and I'm afraid
we'll discover along the way that the representations we use are messy
and don't always obey the subtyping suggested by the error hierarchy).

It presumably needs to come with a "similar" change on the constructor
side, so as long as we keep using (signal ERROR-TYPE ERROR-DATA) where
ERROR-DATA is a bare list it's not super pressing.

>   They shouldn't be terribly hard to find.  From some greps I
>   estimate there less than 700 condition-case that actually use
>   the variable and the majority seems to be resignalling or
>   printing.  The former will probably be fixed by
>   handler-bind anyway, and printing is a generic operation
>   already.  I checked about 20 occurances randomly, and they
>   all fell into this basket.  This also tells me that switching
>   to proper objects isn't that hard.

Even better,


        Stefan




^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  2023-12-29  3:43                                 ` João Távora
  2023-12-29 16:54                                   ` Stefan Monnier
@ 2023-12-29 17:19                                   ` Alan Mackenzie
  2023-12-29 17:24                                     ` João Távora
  1 sibling, 1 reply; 43+ messages in thread
From: Alan Mackenzie @ 2023-12-29 17:19 UTC (permalink / raw)
  To: João Távora; +Cc: Stefan Monnier, emacs-devel

Hello, João.

I'll admit I've not been following this thread closely, but, ...

On Fri, Dec 29, 2023 at 03:43:54 +0000, João Távora wrote:
> On Fri, Dec 29, 2023 at 2:55 AM Stefan Monnier <monnier@iro.umontreal.ca> wrote:

> > > What object exactly goes into the handler bind handlers?

> > The same (even `eq`) as the one passed to the `condition-case` handler.

> Would it matter if it weren't?

> > > Is it the funny cons?

> > Yup.  That's ELisp's current notion of an "error object".

> > > Can we at least get some generic functions to hide that representation
> > > so that we can change it later on?

No, no, no.  Please.  (See below.)

> > Patch very welcome, yes (and this is orthogonal to `handler-bind`).

> What operations are typically requested from it?

> - Printing?  a cl-print-object would be sufficient, I think,
>   along with a (orthogonal) directive to 'format' for using
>   cl-prin1 (why doesn't cl-princ exist?)

> - Getting the type for resignalling purposes?  Unfortunately,
>   this returns 'cons'

>      (condition-case err (error "bla") (error (type-of err)))

>   Can it be made to return error?

> - Can we import a typep (similar to cl-typep) that understands
>   basic types EIEIO and also error hierarchy?

> - simple-condition-format-control and
>   simple-condition-format-arguments?

> - change manual to explain that resignalling is discouraged now
>   that handler-bind exists, but if resignalling _is_ done, then
>   definitely avoid car and cdr and some form of

>     (signal (error-symbol err) (error-data err)

>   introducing these new functions?

> - go through the hierarchy and add <error-name>-<data-slot>
>   accessors? Or do we already consider these details of "data" to
>   to be hidden?  If so, I think surely some violations exist,
>   hopefully not many.

>   They shouldn't be terribly hard to find.  From some greps I
>   estimate there less than 700 condition-case that actually use
>   the variable and the majority seems to be resignalling or
>   printing.  The former will probably be fixed by
>   handler-bind anyway, and printing is a generic operation
>   already.  I checked about 20 occurances randomly, and they
>   all fell into this basket.  This also tells me that switching
>   to proper objects isn't that hard.

Talk about "generic functions" and "proper objects" makes me worried
indeed.  The Emacs error handling must work fully right from early
bootstrap, i.e. when no Lisp has yet been loaded.  The "proper object"
for this is a list, of defined structure.  This is approximately what we
have at the moment.  It would be useful to tighten up this definition.

> João

-- 
Alan Mackenzie (Nuremberg, Germany).



^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  2023-12-29 17:19                                   ` Alan Mackenzie
@ 2023-12-29 17:24                                     ` João Távora
  2023-12-29 17:43                                       ` Alan Mackenzie
  0 siblings, 1 reply; 43+ messages in thread
From: João Távora @ 2023-12-29 17:24 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: Stefan Monnier, emacs-devel

On Fri, Dec 29, 2023 at 5:19 PM Alan Mackenzie <acm@muc.de> wrote:

> Talk about "generic functions" and "proper objects" makes me worried
> indeed.  The Emacs error handling must work fully right from early
> bootstrap, i.e. when no Lisp has yet been loaded.  The "proper object"
> for this is a list, of defined structure.

It's indistinguishable from a cons, which is bad for many reasons
explained at length.  Weren't you involved in "symbol-with-pos"?
It's a similar object with a unique type.  So it's going to need
more arguments than just being scared of generic functions.
Noone is advocating for calling generic functions from C or
preloading them, etc, so don't worry about it.



^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  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
  0 siblings, 2 replies; 43+ messages in thread
From: João Távora @ 2023-12-29 17:29 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

On Fri, Dec 29, 2023 at 4:54 PM Stefan Monnier <monnier@iro.umontreal.ca> wrote:
>
> >> > What object exactly goes into the handler bind handlers?
> >> The same (even `eq`) as the one passed to the `condition-case` handler.
> > Would it matter if it weren't?
>
> I think we want to preserve the identity of an error as it
> passes through its various handlers, yes.
> That makes it possible to use `eq` hash tables and to mutate them.

First, I think that's a bad idea for the reasons I explained
at length.  You should "remember" errors via restarts.

Most importantly, and idea quality apart, who can be doing that
today, before h-b?  No-one or practically no-one, since it's useless
because currently the only place user code has access to
the signal also unwinds the stack, and resignalling will make a new
cons. Right?

So unless there's a specific use for it today, I recommend
specifically _not_ maintaining 'eq'ness.  This prevents
people from making the job to transition to a non-cons
representation of errors easier.

> > - Printing?  a cl-print-object would be sufficient, I think,
> >   along with a (orthogonal) directive to 'format' for using
> >   cl-prin1 (why doesn't cl-princ exist?)
>
> We cannot reliably distinguish an error object from random other cons
> cells, so things like `cl-print-object` are simply not an option: the
> caller needs to tell us that this is an error object.
> We could have a `print-error-object`, OTOH.

Indeed this representation is really bothersome.

> > - Getting the type for resignalling purposes?  Unfortunately,
> >   this returns 'cons'
> >
> >      (condition-case err (error "bla") (error (type-of err)))
> >
> >   Can it be made to return error?
>
> If it's only for resignaling purposes, why not have
> `error-resignal` instead which takes the error object itself?

This has to exist indeed.  Should be called "condition-resignal"
though (and the byte-compiler should warn you should use a
handler-bind instead).

> > - Can we import a typep (similar to cl-typep) that understands
> >   basic types EIEIO and also error hierarchy?
>
> Nope for the reason mentioned above.
> But we could have a `error-typep`.  AFAIK this kind of operation is very
> rarely used in ELisp (it's only used internally in the C code which
> looks for the appropriate handler).

Let's hope so.  Skipping that then.

> > - simple-condition-format-control and
> >   simple-condition-format-arguments?
>
> I can't think of any ELisp code I've encountered that does something
> related, so I think it's a YAGNI.

Hard to add with that random data bag'o'things anyway.

> BTW, the best way to find what we need is probably to change the
> `signal_or_quit` code which does the `Fcons (error_symbol, data)` and
> make it build something else than a cons instead.

True!  Let's just preload EIEIO and call make-instance.  Ahaha

> Then go and fix all the things that break, introducing new `error-FOO`
> abstractions along the way.
>
> > - change manual to explain that resignalling is discouraged now
> >   that handler-bind exists, but if resignalling _is_ done, then
> >   definitely avoid car and cdr and some form of
> >
> >     (signal (error-symbol err) (error-data err)
> >
> >   introducing these new functions?
>
> We first want to help people make their existing code compatible with
> other representations of error objects which minimal changes.
> Changing the code to use `error-resignal` is trivial whereas making it
> use `handler-bind` can be difficult since the semantics is subtly
> different and it's not always easy to judge whether the difference
> matters in this particular case.

Makes sense, though 100% of the cases I've seen for resignalling are
equivalent functionally to a handler-bind.

> > - go through the hierarchy and add <error-name>-<data-slot>
> >   accessors?
>
> I don't think we want to do that systematically, but yes (and I'm afraid
> we'll discover along the way that the representations we use are messy
> and don't always obey the subtyping suggested by the error hierarchy).
>
> It presumably needs to come with a "similar" change on the constructor
> side, so as long as we keep using (signal ERROR-TYPE ERROR-DATA) where
> ERROR-DATA is a bare list it's not super pressing.

I've also seen 0 examples so far where the DATA is searched inside
for actual properties.

João



^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  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
  1 sibling, 0 replies; 43+ messages in thread
From: João Távora @ 2023-12-29 17:39 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

On Fri, Dec 29, 2023 at 5:29 PM João Távora <joaotavora@gmail.com> wrote:
>
> On Fri, Dec 29, 2023 at 4:54 PM Stefan Monnier <monnier@iro.umontreal.ca> wrote:
> >
> > >> > What object exactly goes into the handler bind handlers?
> > >> The same (even `eq`) as the one passed to the `condition-case` handler.
> > > Would it matter if it weren't?
> >
> > I think we want to preserve the identity of an error as it
> > passes through its various handlers, yes.
> > That makes it possible to use `eq` hash tables and to mutate them.
>
> First, I think that's a bad idea for the reasons I explained
> at length.  You should "remember" errors via restarts.

More precisely, even restarts are not necessary.  One should
remember errors by not forgetting about them!  I.e. use
handler-bind.

João



^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  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:35                                         ` Stefan Monnier
  0 siblings, 2 replies; 43+ messages in thread
From: Alan Mackenzie @ 2023-12-29 17:43 UTC (permalink / raw)
  To: João Távora; +Cc: Stefan Monnier, emacs-devel

Hello, João.

On Fri, Dec 29, 2023 at 17:24:29 +0000, João Távora wrote:
> On Fri, Dec 29, 2023 at 5:19 PM Alan Mackenzie <acm@muc.de> wrote:

> > Talk about "generic functions" and "proper objects" makes me worried
> > indeed.  The Emacs error handling must work fully right from early
> > bootstrap, i.e. when no Lisp has yet been loaded.  The "proper object"
> > for this is a list, of defined structure.

> It's indistinguishable from a cons, which is bad for many reasons
> explained at length.

But it's a good choice for early bootstrap.  How about dealing with
those reasons in other ways rather than using not-yet-existent data
structures.

> Weren't you involved in "symbol-with-pos"?

I developped them, yes.

> It's a similar object with a unique type.

Similar to what?

> So it's going to need more arguments than just being scared of generic
> functions.

There's no fear involved at all.  cl-lib is an exceptionally
controversial part of Emacs, as we established two or three weeks ago.
It's poorly documented, and likely buggy.  It's not something to be
moved to the centre of the Emacs Lisp machine.

> Noone is advocating for calling generic functions from C or preloading
> them, etc, so don't worry about it.

That's got no connection with what I wrote.  What I'm asking you to
confirm is that you're not contemplating introducing error handling
machinery which, during bootstrap, will only work after Lisp code has
been loaded.

-- 
Alan Mackenzie (Nuremberg, Germany).



^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  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:35                                         ` Stefan Monnier
  1 sibling, 1 reply; 43+ messages in thread
From: João Távora @ 2023-12-29 17:54 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: Stefan Monnier, emacs-devel

On Fri, Dec 29, 2023 at 5:43 PM Alan Mackenzie <acm@muc.de> wrote:

> > It's a similar object with a unique type.
>
> Similar to what?

The object that signal() would craft and appear to Lisp is similar to how
symbol-with-pos is passed to, say, compiler macros (breaking some of
them, last I remember, but that's besides our discussion).

> > So it's going to need more arguments than just being scared of generic
> > functions.
>
> There's no fear involved at all.  cl-lib is an exceptionally
> controversial part of Emacs, as we established two or three weeks ago.
> It's poorly documented, and likely buggy.  It's not something to be
> moved to the centre of the Emacs Lisp machine.

generic functions aren't in cl-lib, and, once again, noone is proposing
doing anything of the sort, so why you bring cl-lib up is mystery to me.

> > Noone is advocating for calling generic functions from C or preloading
> > them, etc, so don't worry about it.
>
> That's got no connection with what I wrote.  What I'm asking you to
> confirm is that you're not contemplating introducing error handling
> machinery which, during bootstrap, will only work after Lisp code has
> been loaded.

I think I can confirm that yes.

João



^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  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
  0 siblings, 1 reply; 43+ messages in thread
From: Alan Mackenzie @ 2023-12-29 18:08 UTC (permalink / raw)
  To: João Távora; +Cc: Stefan Monnier, emacs-devel

Hello, João.

On Fri, Dec 29, 2023 at 17:54:30 +0000, João Távora wrote:
> On Fri, Dec 29, 2023 at 5:43 PM Alan Mackenzie <acm@muc.de> wrote:

> > > It's a similar object with a unique type.

> > Similar to what?

> The object that signal() would craft and appear to Lisp is similar to how
> symbol-with-pos is passed to, say, compiler macros (breaking some of
> them, last I remember, but that's besides our discussion).

Ah, right!  So you're considering, possibly, a new type of pseudovector,
or something similar.

> > > So it's going to need more arguments than just being scared of generic
> > > functions.

> > There's no fear involved at all.  cl-lib is an exceptionally
> > controversial part of Emacs, as we established two or three weeks ago.
> > It's poorly documented, and likely buggy.  It's not something to be
> > moved to the centre of the Emacs Lisp machine.

> generic functions aren't in cl-lib, and, once again, noone is proposing
> doing anything of the sort, so why you bring cl-lib up is mystery to me.

The word "generic" was mentioned, which makes me think of cl-generic.el,
a file I'm struggling quite badly with, to debug.  By cl-lib, I meant
cl-*.el.  Sorry for not being more exact.

> > > Noone is advocating for calling generic functions from C or preloading
> > > them, etc, so don't worry about it.

> > That's got no connection with what I wrote.  What I'm asking you to
> > confirm is that you're not contemplating introducing error handling
> > machinery which, during bootstrap, will only work after Lisp code has
> > been loaded.

> I think I can confirm that yes.

Thanks!

> João

-- 
Alan Mackenzie (Nuremberg, Germany).



^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  2023-12-29 17:43                                       ` Alan Mackenzie
  2023-12-29 17:54                                         ` João Távora
@ 2023-12-29 18:35                                         ` Stefan Monnier
  2023-12-29 18:48                                           ` João Távora
  1 sibling, 1 reply; 43+ messages in thread
From: Stefan Monnier @ 2023-12-29 18:35 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: João Távora, emacs-devel

> That's got no connection with what I wrote.  What I'm asking you to
> confirm is that you're not contemplating introducing error handling
> machinery which, during bootstrap, will only work after Lisp code has
> been loaded.

The idea is:

- In the short term, provide new functions that allow manipulating
  errors in a way that's independent from their representation
  (as cons cells).
  Hopefully this will make the code also more readable in many cases.
  E.g. (error-resignal ERR) instead of (signal (car ERR) (cdr ERR)).

- In the longer term once enough code has been adapted to use the new
  functions, we may be able to consider changing the representation of
  error objects.  Presumably error objects would then be changed from

      (list ERROR-TYPE DATA1 DATA2 ...)

  to something like

      (record ERROR-TYPE DATA1 DATA2 ...)

  This shouldn't require anything tricky during bootstrapping.
  But in any case we're pretty damn far from this.  I'd estimate that we
  have at least 10 years ahead of us to decide if and how we want to
  make that change.


-- Stefan




^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  2023-12-29 18:08                                           ` Alan Mackenzie
@ 2023-12-29 18:45                                             ` João Távora
  0 siblings, 0 replies; 43+ messages in thread
From: João Távora @ 2023-12-29 18:45 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: Stefan Monnier, emacs-devel

On Fri, Dec 29, 2023 at 6:08 PM Alan Mackenzie <acm@muc.de> wrote:
>
> Hello, João.
>
> On Fri, Dec 29, 2023 at 17:54:30 +0000, João Távora wrote:
> > On Fri, Dec 29, 2023 at 5:43 PM Alan Mackenzie <acm@muc.de> wrote:
>
> > > > It's a similar object with a unique type.
>
> > > Similar to what?
>
> > The object that signal() would craft and appear to Lisp is similar to how
> > symbol-with-pos is passed to, say, compiler macros (breaking some of
> > them, last I remember, but that's besides our discussion).
>
> Ah, right!  So you're considering, possibly, a new type of pseudovector,
> or something similar.

Yes, exactly, something of the sort.

João



^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  2023-12-29 18:35                                         ` Stefan Monnier
@ 2023-12-29 18:48                                           ` João Távora
  0 siblings, 0 replies; 43+ messages in thread
From: João Távora @ 2023-12-29 18:48 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Alan Mackenzie, emacs-devel

On Fri, Dec 29, 2023 at 6:35 PM Stefan Monnier <monnier@iro.umontreal.ca> wrote:
>
> > That's got no connection with what I wrote.  What I'm asking you to
> > confirm is that you're not contemplating introducing error handling
> > machinery which, during bootstrap, will only work after Lisp code has
> > been loaded.
>
> The idea is:
>
> - In the short term, provide new functions that allow manipulating
>   errors in a way that's independent from their representation
>   (as cons cells).
>   Hopefully this will make the code also more readable in many cases.
>   E.g. (error-resignal ERR) instead of (signal (car ERR) (cdr ERR)).
>
> - In the longer term once enough code has been adapted to use the new
>   functions, we may be able to consider changing the representation of
>   error objects.  Presumably error objects would then be changed from
>
>       (list ERROR-TYPE DATA1 DATA2 ...)
>
>   to something like
>
>       (record ERROR-TYPE DATA1 DATA2 ...)
>
>   This shouldn't require anything tricky during bootstrapping.
>
>   But in any case we're pretty damn far from this.  I'd estimate that we
>   have at least 10 years ahead of us to decide if and how we want to
>   make that change.

I concur.  And maybe, to keep 100% backward compatibility to
current Lisp code, condition-case is AFAIK is the only current
to get at error objects from Lisp, would still get the
good-ol cons  representation for those 10 years, or even forever.

João



^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  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
  1 sibling, 1 reply; 43+ messages in thread
From: Stefan Monnier @ 2023-12-30  4:29 UTC (permalink / raw)
  To: João Távora; +Cc: emacs-devel

> Most importantly, and idea quality apart, who can be doing that
> today, before h-b?  No-one or practically no-one, since it's useless
> because currently the only place user code has access to
> the signal also unwinds the stack, and resignalling will make a new
> cons. Right?

Indeed, until now the identity of an error object can't be "noticed".
The only place other than the `condition-case` where an error can appear
is in `signal-hook-function` and this doesn't receive the error object
(it receives it in two pieces instead 🙁).

> So unless there's a specific use for it today,

Just because we can't think of a use case today doesn't mean there can't
be a good use case.  And yes, I do have some use cases.  Those use cases
may be able to use other approaches as well, but the various options all
have pros and cons.

> I recommend specifically _not_ maintaining 'eq'ness.
> This prevents people from making the job to transition to a non-cons
> representation of errors easier.

You lost me here.

>> We cannot reliably distinguish an error object from random other cons
>> cells, so things like `cl-print-object` are simply not an option: the
>> caller needs to tell us that this is an error object.
>> We could have a `print-error-object`, OTOH.
> Indeed this representation is really bothersome.

Actually, it's not.  We've lived with it quite happily for about 40
years now.  In practice, we really don't do much with error objects
other than re-signal them or pass them to `error-message-string`.

>> If it's only for resignaling purposes, why not have
>> `error-resignal` instead which takes the error object itself?
> This has to exist indeed.  Should be called "condition-resignal"
> though

Emacs's naming convention in this area is a bit murky, but I think it's
leaning towards `error` (e.g. the `error-message-string` function, the
`define-error` macro, the `error-conditions` and `error-message`
properties).
This said, as long as the new functions are nicely placed together in
their own namespace prefix, I'll be happy, so if you prefer
`condition-`, I can go along with that.

> (and the byte-compiler should warn you should use a
> handler-bind instead).

As I mentioned elsewhere, running code inside a `handler-bind` means
running in some arbitrary dynamic context, which can have surprising
effects because in ELisp dynvars influence the execution of a *lot* of
primitives (even `eq` is not immune).  So I don't think I can honestly
claim that re-signaling an error is almost always wrong.  Maybe with
more practice, I'll reach this conclusion, but I'm definitely not
there yet.  So, warning about resignaling seems premature.

>> I don't think we want to do that systematically, but yes (and I'm afraid
>> we'll discover along the way that the representations we use are messy
>> and don't always obey the subtyping suggested by the error hierarchy).
>> It presumably needs to come with a "similar" change on the constructor
>> side, so as long as we keep using (signal ERROR-TYPE ERROR-DATA) where
>> ERROR-DATA is a bare list it's not super pressing.
> I've also seen 0 examples so far where the DATA is searched inside
> for actual properties.

That's my impression as well.
The fact that the "data" part has no clearly defined shape (other than
being a list) means that it's used a bit "without purpose": I think that
in most cases, programmers put data in there only under the expectation
that it'll be useful when displayed in an error message or a backtrace.


        Stefan




^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Attaching context info to an error
  2023-12-30  4:29                                       ` Stefan Monnier
@ 2023-12-30 16:45                                         ` João Távora
  0 siblings, 0 replies; 43+ messages in thread
From: João Távora @ 2023-12-30 16:45 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

On Sat, Dec 30, 2023 at 4:29 AM Stefan Monnier <monnier@iro.umontreal.ca> wrote:
>
> > Most importantly, and idea quality apart, who can be doing that
> > today, before h-b?  No-one or practically no-one, since it's useless
> > because currently the only place user code has access to
> > the signal also unwinds the stack, and resignalling will make a new
> > cons. Right?
>
> Indeed, until now the identity of an error object can't be "noticed".
> The only place other than the `condition-case` where an error can appear
> is in `signal-hook-function` and this doesn't receive the error object
> (it receives it in two pieces instead 🙁).

Indeed, but that's a good thing IMO.  It tells us something we don't
_have_ to do, which is always good from a language design evolution
point of view.

> Emacs's naming convention in this area is a bit murky, but I think it's
> leaning towards `error` (e.g. the `error-message-string` function, the
> `define-error` macro, the `error-conditions` and `error-message`
> properties).
> This said, as long as the new functions are nicely placed together in
> their own namespace prefix, I'll be happy, so if you prefer
> `condition-`, I can go along with that.

You're right, so I can go along with `error-` just fine.  We can
make a zillion aliases later :-) (or not).

> > (and the byte-compiler should warn you should use a
> > handler-bind instead).
>
> As I mentioned elsewhere, running code inside a `handler-bind` means
> running in some arbitrary dynamic context, which can have surprising
> effects because in ELisp dynvars influence the execution of a *lot* of
> primitives (even `eq` is not immune).  So I don't think I can honestly
> claim that re-signaling an error is almost always wrong.  Maybe with
> more practice, I'll reach this conclusion, but I'm definitely not
> there yet.  So, warning about resignaling seems premature.

Resignalling a different error is fine, it's sometimes needed.
Can be just a different error message.
Resignalling the very same error, without changes, is always a
fairly bad code smell (in handler-bind-capable languages,
in C++ and current Elisp you have no other option).

> The fact that the "data" part has no clearly defined shape (other than
> being a list) means that it's used a bit "without purpose": I think that
> in most cases, programmers put data in there only under the expectation
> that it'll be useful when displayed in an error message or a backtrace.

I'm the exception. :-D  I stashed some info in the jsonrpc-error data in
hopes they would become useful for things other than printing
when handler-bind arrives.  And then there's this <grimaces>

(condition-case err
            (electric-pair--with-syntax string-or-comment
              (scan-sexps (point) (if (> direction 0)
                                      (point-max)
                                    (- (point-max))))
              (funcall at-top-level-or-equivalent-fn))
          (scan-error
           (cond ((or
                   ;; some error happened and it is not of the "ended
                   ;; prematurely" kind...
                   (not (string-match "ends prematurely" (nth 1 err)))
                   ;; ... or we were in a comment and just came out of
                   ;; it.
                   (and string-or-comment
                        (not (nth 8 (syntax-ppss)))))
                  (funcall at-top-level-or-equivalent-fn))
                 (t
                  ;; exit the sexp
                  (goto-char (nth 3 err))
                  (funcall ended-prematurely-fn)))))

Also me :-)  but I really have doubts I'm the only one.



^ permalink raw reply	[flat|nested] 43+ messages in thread

end of thread, other threads:[~2023-12-30 16:45 UTC | newest]

Thread overview: 43+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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
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

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).