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

Wrap built-in types like dictionary? #565

Closed
maranmaran opened this issue Nov 21, 2024 · 6 comments
Closed

Wrap built-in types like dictionary? #565

maranmaran opened this issue Nov 21, 2024 · 6 comments

Comments

@maranmaran
Copy link

Hi,

Is it possible to wrap the provided built-ins like map {}
I wish for example to catch all key-access related issues to map {}

Example code:

mymap = {}
mymap["non-existing"] <- returns error

Interpreting EvalError feels hackish because it's not that granual. EvalError could be plenty things.
I wonder if there's a way to decorate or provide some kind of middleware to tune in to these data structures.

Or at least react on eval errors with more precision. Inspecting string seems a bit clunky to me because it highly depends on returned internals from the lib.

The error type EvalError is too broad to narrow down specific data-structures and problems.

Any help here would be appreciated :) Thanks!

@adonovan
Copy link
Collaborator

I'm not quite sure what you're asking for. Is it that you want to change the meaning of {} (and presumably other operations too) so that instead of creating a dict, it creates a value of some other user-defined data type that would allow you to intercept operations on the dict? If so: no, there is no way to do that---at least, not without modifying the interpreter.

What is your broader goal?

@maranmaran
Copy link
Author

maranmaran commented Nov 21, 2024

@adonovan Thanks for reply, let me clarify 😄

I wish for a way to catch some runtime or compile time errors like:

  • index-accessing a key that doesn't exist
  • not existing property or method
  • some other specific ones

and handle them in a strongly-typed way, perhaps even wrapping them in my own error definition.

CallInternal will return EvalError, but there may be many different kinds of errors there, right.?

I'm looking for a way to intercept these and handle them in some concrete fashion.
So far the easiest and only way I could do that is regex matching the EvalError message or is there something I'm missing?

However, I'm not a fan of that because if these error messages internal to the library were to change it would be hard for me to catch that.

I'm not quite sure what you're asking for. Is it that you want to change the meaning of {} (and presumably other operations too) so that instead of creating a dict, it creates a value of some other user-defined data type that would allow you to intercept operations on the dict? If so: no, there is no way to do that---at least, not without modifying the interpreter.

Yes, I was looking for a way to perhaps decorate and handle some stuff in between for specific data structures, symbols, etc.
I want to respect the meaning and allow myself to just plug-in with some handlers and interpret things in a more specific way, as I know what I'd be handling at the moment.

struct MyDict {
   starlark.Dict
}

func (d *MyDict) Get() {
    // custom code before
     v, err := d.Get()
    // custom code after
    return v, err
}

@adonovan
Copy link
Collaborator

I wish for a way to catch some runtime or compile time errors like [...] and handle them in a strongly-typed way, perhaps even wrapping them in my own error definition.

There is no way to do that today. (See my last comment below.)

CallInternal will return EvalError, but there may be many different kinds of errors there, right.?

Correct. EvalError doesn't currently return any structure about the error. It could be changed to do so, but it would be a lot of work and it is not clear to me what exactly a client could reliably do with extra information. And of course existing application code would continue to return unstructured errors indefinitely.

I'm looking for a way to intercept these and handle them in some concrete fashion.
So far the easiest and only way I could do that is regex matching the EvalError message or is there something I'm missing?
However, I'm not a fan of that because if these error messages internal to the library were to change it would be hard for me to catch that.

Right, pattern matching is the best that you can do today, and it is fragile, but it is not clear to me that structured errors are fundamentally less fragile. Changes to the interpreter can always alter the order in which preconditions are checked, and thus the particular error observed for a given program with more than one problem. Changes that refine some general error into multiple specific errors might also break the implied structured-error interface.

I was looking for a way to perhaps decorate and handle some stuff in between for specific data structures, symbols, etc.

Can you give more detail? You are still talking about implementations but I don't understand what problem it is that you are trying to solve.

@maranmaran
Copy link
Author

Can you give more detail? You are still talking about implementations but I don't understand what problem it is that you are trying to solve.

Have a layer on top of this lib and run some client defined code. So far this code could have been faulty in runtime which was supported business case. Now I wish to tailor the experience add-in stricter checks and evaluation so things don't go south and they're alerted or at least get some verification. I wish to measure and see what kind of problems are my clients facing in runtime and offer some better experience around that. For that it'd be great if I could know some more on the structure of the evaluation errors.

@adonovan
Copy link
Collaborator

So far this code could have been faulty in runtime which was supported business case.

Do you mean that evaluation of the user's Starlark program fails because of a mistake, and the stack (from EvalErr.Backtrace) is not sufficient for them to understand what went wrong (or at least suggest the next step to get closer to understanding)?

Now I wish to tailor the experience add-in stricter checks and evaluation so things don't go south and they're alerted or at least get some verification.

It sounds like you are describing the general problem of debugging programs in untyped languages (like Python, Ruby, JavaScript). There are well established techniques for making progress in the face of a run-time error---essentially, add more assertions, and write more tests--but ultimately the reliability of all dynamic languages suffers from the lack of types, which is why they tend to grow bolt-on type systems (such as PyType for Python, Sorbet for Ruby, and Closure for JavaScript). You may be interested in bazelbuild/starlark#106 and https://docs.google.com/document/d/1Sid7EAbBd_w_T7D94Li_f_bK3zMTztFbzIMvcpzo1wY, proposals for types in Starlark.

I wish to measure and see what kind of problems are my clients facing in runtime and offer some better experience around that. For that it'd be great if I could know some more on the structure of the evaluation errors.

Without changing the interpreter, you could pattern-match on various error strings and increase a counter each time a program crashes for a given error message, then collect statistics about these counters. That would be the most expedient way to measure.

Alternatively, you could fork the interpreter used by your application to add more structure to EvalErr. That would be less convenient but would give you more control.

Given that this is just an experiment, I don't think there is a clear goal or proposal here for changing the interpreter, so I'm going to close this issue.

@maranmaran
Copy link
Author

maranmaran commented Nov 24, 2024

That's fair, thanks for taking time to discuss, I appreciate it. @adonovan

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

2 participants