Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error & error message #168

Open
sorawee opened this issue Aug 27, 2021 · 4 comments
Open

Error & error message #168

sorawee opened this issue Aug 27, 2021 · 4 comments

Comments

@sorawee
Copy link
Contributor

sorawee commented Aug 27, 2021

A couple of unorganized thoughts:

  • I really like the error message format of raise-arguments-error, but its name suggests or gives an impression that its application is very limited. Can we improve this?
  • Also, would be really nice to have a variant of raise-arguments-error that doesn't show stacktrace (a la raise-user-error)
  • raise-argument-error's expected is usually a contract written in a string literal. Whenever it's more than one line, string-append with a bunch of \n and padding is usually needed, which is not ideal. Would be nice to have something like Scribble's defproc that formats the output according to the source code.
  • I think DrRacket's coloring the whole error message as red is unappealing.

Nice stuff from other modern languages:

  • Command-line could use some color.
  • In addition to line, column, and source, show the actual file content with highlight / indicator.
  • Suggestion on how to fix the problem and/or FAQ for the error.
@samdphillips
Copy link
Contributor

Some related thoughts I've had on exceptions.

I find that a majority of Racket exceptions just being a struct type with a message and the current marks is a bit limiting. If a developer wanted to add some useful fields they would need to make a constructor to deal with boilerplate formatting of the message including their addtional information, etc.

On the other end if a program wanted to hide some details of an exception it would either need to change the error display handler, or eat the exception and replace it with a "simpler" value.

@rocketnia
Copy link

In a spirit of excess ambition, I think there's a bit of a model-view separation to be explored here. Each error is an instance of its own unique type (some of which may share some interfaces), and the act of displaying an error of each type, for each user audience (e.g. different experience levels, different locales...), for each development environment (e.g. DrRacket, command line, racket-mode, LSP...) is a 3D Expression Problem. The function implementation that raises the error can hardly be expected to anticipate every view, so its core responsibility is to construct a model they can all use. (A plain text error message string is something they can all potentially use, but it may not be as usable as it could be.)

Whenever one error-prone abstraction calls another, in general it should supply a propagation function that translates from the callee's error model type to the caller's. If it doesn't opt to provide a translation, the error is blamed on that call site. The translation applies even to errors that don't happen right away. I believe this should facilitate higher-order contracts.

Contract combinators built around this would tend to use translation functions that did something like blame-add-context, but instead of using a string, they could represent the context information in a way that could be more easily parsed and translated. Currently, when one contract combinator is defined in terms of others using rename-contract, the blame-add-context information doesn't get "renamed," but this should make that feasible to do. It could also facilitate rendering the blame context as a highlight or arrow indicating the particular subexpression of the contract that was violated, in cases where that made sense.

I expect this to be a far more compelling system for error propagation than dynamically scoped handlers are. Errors are part of the front-end-visible behavior of the code that causes them, like the syntax is. Reckless abstraction over the front end of the code (like using several layers of macros that aren't careful to use syntax/loc) leads to a degraded error experience, so I've lately been thinking error-prone functions are in some sense less abstractable than other functions (e.g. ones that have already been statically typechecked to rule out errors). This model-view separation and model translation approach could make up for some of that.

This idea refines a messier incarnation I've explored with Cene(-for-Racket), where I give every function call a first-class reference to a source location (usually of its own call site) that it can pin its errors on. I started making source location combinators to carry more information in the errors and to get a sense of what kind of error propagation patterns I was using, but as of the last time I worked on Cene, it didn't occur to me that I was really trying to express different translation policies.

(To really digress: Perhaps there's something to those combinators. A translation between one model and another isn't necessarily a one-way function. If an error message is interactive, there could be a whole model-view-controller setup, and the model-to-model translation could compute in both directions. Perhaps treating a translation policy as a nominally typed object with its own structured combinator language could make arbitrary interaction protocols translatable, at the cost of adding another dimension to that Expression Problem.)

@jackfirth
Copy link
Collaborator

jackfirth commented Sep 21, 2021

I think some of these problems can be fixed by having a function in Racket for building error messages in the correct format according to racket's error message conventions:

; returns an immutable string
(build-error-message
  #:source srcloc  ; optional
  name
  message
  #:continued-message continued-message  ; optional
  field detail ... ...)

@samdphillips
Copy link
Contributor

Does #550 cover what folks are thinking here?

It seems to me that a 4-argument exn constructor and having the Clause class be nonfinal allows for attaching metadata to exceptions.

Attaching color information may still not be easy with that PR though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants