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
[asdl/runtime] Add deriving clause / Use ASDL for the ControlFlow exception type #1681
base: soil-staging
Are you sure you want to change the base?
Conversation
We can now make asdl types derive arbitrarily like so: ```asdl module runtime { control_flow = Break(int levels) | Return(int code) deriving [Exception] } ```
Hm very interesting I have been wondering about Though I think we have to be careful to tease out these 2 issues
So it's sort of another types vs. tags issue Or maybe composition vs. inheritance |
Another thing I'm thinking about is whether
A keyword can't be redefined, but a builtin can We could introduce an option to parse https://github.com/oilshell/oil/wiki/Language-Design-Principles |
Perhaps a deriving clause is too general of a solution. If we instead added an In retrospect, I am not sure what else we'd want a
I am not sure what the issue is here exactly? Isn't this what we were trying to achieve with
Really good point. That's a tricky problem. If I were to approach this entirely from a UX perspective, it might make sense that:
Now we would need to draw the line on a more concrete But, to be honest, I am not sure how much I like the fact that One alternative would be to shove more behavior into |
The interesting thing is that the way the code is now, we're not using vm.ControlFlow() at all, it's not a base class It looks like we could just have
There's no inheritance at all, and no sum type! Although I guess we were thinking about rewriting some of the except() clauses too? I guess catching exactly what you want is perhaps more efficient than catching and then rethrowing ? Not sure to be honest, I haven't looked at all the call sites It looks like the main thing we're using is |
As far as I was thinking
Generally we don't shy away from adding builtins in plain OSH, because you can always redefine them. It doesn't really break anything; bash adds its own builtins too (e.g. And actually we don't shy away from keywords -- (The reason However, an I understand why we'd want it to be a keyword in pure YSH, but I also think people generally won't notice (??) |
To clarify, this comment which I guess I wrote quite awhile ago, before implementing anything https://github.com/oilshell/oil/blob/master/core/vm.py#L38 suggests that we might want to have cflow.Shell and cflow.OilReturn() have a common base class ( But it appears we don't actually need that anywhere? |
Wow! That was actually a mistake on my part, I had meant for
Yes, this is the advantage to having separate control flow exceptions for each keyword. It's technically wasteful because, as we did before, it is possible to determine control flow behavior from the keyword tag. But then we can lift the burden of selection from the user by leaning on language features. ie.
can then more concisely be written as:
Now, if we wanted to raise an assertion error for other
The need for this "wild card" case is missing when, as in
You are right. So many interpretations of I can see there being confusion over the identity of Making
And, if we make sure to document that |
I would start from the raw If it ends up being 3 exceptions that have no common base class, that's OK Or if it ends up with a common base class, that's also OK (generally I avoid inheritance unless it adds something, like the fallthrough -- and there is some stuff we might want to revisit in The class relationships should be derived from the behavior. I think I can definitely see that removing IsReturn() could be cleaner, but it's also true that subclassing is limited in what it can model. We are stuck with a certain kind of subclassing because C++ and Python support it (in much the same way, which is nice) But there's also the problem that in Python you can do this:
but in C++ , there's no way to do that -- you have to duplicate the blocks for each case
which is bad So if statements around are actually more expressive than what C++ provides (BTW this is what the Go language designers mean when they say "errors logic is just program logic" and things like that. They don't like the way error handling in Python and C++ is tied to inheritance, which is a fair point.) |
The issue I'm thinking about is if we do:
(which I suggested, maybe wrongly! Since we didn't need the type relationship apparently!) OR we do this:
And then have a non-exception type
This is basically using composition over inheritance, and that seems good -- if we're not getting mileage out of catching exceptions on subtype / subertype It's a bit complex because in some cases like We could have an extra layer like Here the extra layer of composition doesn't seem bad |
You are right. This feels so backwards from what I am used to, I need to do it more. Traditionally I've been taught that behavior is to be molded around some "best" (usually the existing) structure. But I suppose the resulting hacks to make it work often are an issue, not with the behavior, but the structure around it. There are some good ideas here, I am going to start on actually implementing an |
I'm looking over the existing code, and I think there has always been some confusion of catching based on types and types as the shape of the error (what data it holds, e.g. int or value). Those 2 things aren't necessarily the same, but Python and C++ kind of encourage you to make them the same So yeah I think it makes sense to do the minimum thing for Brainstorming, we have 6 kinds of control flow!
Hm I actually think we need to design Maybe you can start a thread on Zulip with a bunch of cases to put in the spec tests? And I will add some as well I actually think it was a happy accident that we discovered the 2 kinds of control flow don't need to inherit from each other. It does make sense when I look at those relationships above At the very least, I think break/continue belong together under the same type, but |
Oh and it's also valid to just plow ahead with And defer Popping up another level, the YSH design should be driven by all the YSH code we write -- that's the only way to know if it's right. We just have to write all the kinds of code that users will write, and see how it looks / if it's comprehensible :) We have at least 14 different use cases on the blog, etc. Remember when we did max(), we figured out we need |
We can now make asdl types derive arbitrarily like so:
Now we are able to declare the
ControlFlow
exception type in ASDL. This will make it easier to add more control flow types such aserror ('...')
. It also makesControlFlow
variant handling logic more uniform with other ASDL types.