-
Notifications
You must be signed in to change notification settings - Fork 732
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
SCLang: ClassLib: Implement doesNotUnderstandAbout
preserving kwargs across doesNotUnderstand
calls
#6297
base: develop
Are you sure you want to change the base?
Conversation
This comment was marked as resolved.
This comment was marked as resolved.
5994984
to
1a9c607
Compare
This comment was marked as resolved.
This comment was marked as resolved.
doesNotUnderstandWithKey
s preserving kwargs across doesNotUnderstand
callsdoesNotUnderstandWithKeys
preserving kwargs across doesNotUnderstand
calls
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
0e0606f
to
11d3108
Compare
This comment was marked as resolved.
This comment was marked as resolved.
11d3108
to
b18d8d2
Compare
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
But I am still thinking about how we could just do it with |
This comment was marked as resolved.
This comment was marked as resolved.
Okay here is the current bug, and its not nice... T_Base {
doesNotUnderstand { |selector ...args|
^\base
}
}
T_Child : T_Base {
doesNotUnderstandAbout { |selector, argumentsArray, keywordArgumentPairs|
^\child
}
}
T_Child2 : T_Base {
doesNotUnderstandAbout { |selector, argumentsArray, keywordArgumentPairs|
this.postln;
^\child
}
}
T_Child().foo(1, 2) // returns \base
T_Child().foo(1, 2, bar: 3) // segfaults
T_Child2().foo(1, 2, bar: 3) // returns \child I've been staring at this for a few hours so will have to take a break. |
This comment has been minimized.
This comment has been minimized.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
ad72220
to
2e30a32
Compare
pythoThanks for this amazing feature and providing a very nice summary of all the changes! This will allow to build more complex structures on the fly much more easy! <3
...
Could you elaborate on what would break if we "keep it simple" here? I totally understand the motivation to keep friction low for users, but I think such a legacy-based approach will (and has?) create more headaches for future code and coders. Personally, I would prefer to keep future maintenance and complexity lower by doing a "clean slate" design, rather than creating detours to preserve legacy that makes the code base more and more complex over the years - especially when it operates in such a global space as On another note, I hope this is not de-railing any efforts, has been thought of to extend the This would not just allow to tweak IMO this would be a great addition to the sclang syntax which has not seen any updates in a long time? |
@capital-G Thanks!
I think the current solution is the simplest, but in the past there have been two other concepts of 'simple'. oneIf you had two methods, then all class would need both twoIf you could add an extra argument into three — the current approachI think the current approach, which I appreciate has changed quite a bit, is actually quite simple. It does normal method overriding rules by walking the class hierarchy, but matches either This means the class closest to the object's class has all the control. I think this is the simplest form this PR can take while not breaking user code. One case where this might not do what the user expects is if they override Basically, by doing this, both
It doesn't. Practically, this would need to be tested in develop.
I don't think this will be possible due to how variable args are implemented as they greedily grab everything on the stack. Generally, they are very hard to reason about. Specifically, it isn't possible to put variable args into an array and pass them to the function as, if my understanding is correct, the stack is 'reinterpreted' as an array, allowing you to read right up to the end. This means you can't place the kwargs at the end of the function signature, they'd have to go at the beginning (a breaking change), therefore, you'd have to change how variable args work, and given their complexity, would almost definitely mean breaking user code some where in very subtle complicated ways. I do agree that this would be a better solution as it would engender the second simple solution above, but what I've done here could quite easily be adapted to support this — actually, I've already done the grouping of args and kwargs and put them on the stack, it would be trivial to adopt this in future. |
2e30a32
to
c1b48eb
Compare
This comment was marked as resolved.
This comment was marked as resolved.
Since I can build now, I was able to properly test stuff, runs well here and looks good. I'll make a review for some cosmetics. |
dfe6d6e
to
62201e8
Compare
@telephon Thanks for the latest review, this has been a lot of back and forth but I think it has really improved and looks well integrated, so thanks for looking at this throughout! I think the way the two methods are called internally is now correct and will hopefully be the least surprising? There is a clear description of this in the opening PR description. It follows on from your initial comment on this PR. There are 2 things that need looking at.1 CI passes on mac but fails on Linux.
I'm not that familiar with node proxy, but working on one and not the other is suspicious, any ideas? 2 What to do with
|
|
The two main places where this is used in the class library, Dictionary and EnvironmentRedirct both call this method like this...
...rather than...
Do you think it would be okay to classify the former as a bug rather than a feature then? If so, functionPerformWithKeys can be deleted. Same thing is true for |
62201e8
to
bb3642f
Compare
@telephon I've fixed the segfault, just a typo. It is just the second point with |
Oh no! It is such a haunt. Well, then I think we really have to introduce new methods. So the methods have to go like this, with a simplification: makePerformableArray { |selector, argumentsArray, keywordArgumentPairs|
var method = this.findRespondingMethodFor(seleector);
if(method.isNil) { MethodError(selector, receiver).throw };
^method.makePerformableArray(argumentsArray, keywordArgumentPairs)
}
performWithKeys { |selector, argumentsArray, keywordArgumentPairs|
var argList = if(keywordArgumentPairs.isNil) { argumentsArray } {
this.makePerformableArray(selector, argumentsArray, keywordArgumentPairs)
};
^this.performList(selector, argList)
}
functionPerformWithKeys { |argumentsArray, keywordArgumentPairs|
var argList = if(keywordArgumentPairs.isNil) { argumentsArray } {
this.def.makePerformableArray(selector, argumentsArray, keywordArgumentPairs)
};
^this.functionPerformList(selector, argList)
}
|
@telephon: some thoughts on the implementations you've suggested there.
... but, is There might be something like this happening in This is almost the same issue as figuring out which If |
When calling an unknown method without keyword args the supercollider vm passes all the information back into the language allowing for transparent wrapper classes. This is not the case with keyword arguments whose data is lost. This PR fixes that issue by implementing a new method in Object.sc `doesNotUnderstandAbout {|selector, argumentsArray, keywordArgumentsPairs|..}`. This commit contains all the core mechanisms of that change, and is broken into a couple of parts. By itself this commit is not that useful as it is hard to actually use the new method, the subsequent commits will address this. A huge thank you to @telephon for helping with the design of the class library methods and help documentation!
Implement performWithKeys, valueWithKeys and functionPerformWithKeys, along with error checking in Method/FunctionDef. These three methods are useful when using `doesNotUnderstandAbout` as they provide a way to call a function with both arguments and keyword arguments centralizing all the error checking inside of Method/FunctionDef. These `*WithKeys` methods take an argumentArray and a keywordArgumentPairs array, then check it against the Method or FunctionDef to ensure the keywords are valid and don't overlap with the non-keyword args. Ultimately, these methods are a way to centralize the error checking.
bb3642f
to
7e97e86
Compare
The motivation behind
The |
Purpose and Motivation
When calling an unknown method without keyword args the supercollider vm passes all the information back into the language allowing for transparent wrapper classes. This is not the case with keyword arguments whose data is lost.
This PR fixes that issue by implementing a new method in Object.sc
doesNotUnderstandAbout {|selector, argumentsArray, keywordArgumentsPairs|..}
, creating methods to make using this easier, updating the objectprototyping mechanism, and updating the rest of the class libraryA huge thank you to @telephon who has been invaluable in designing the class library methods and the help documentation!
Fixes #6064.
See #6227 for the messy history!
This PR has two commits: first implement the core mechanisms, second, update the class library.
1 SCLang: ClassLib: implement doesNotUnderstandAbout.
The core mechanisms of
doesNotUnderstandAbout
.doesNotUnderstandAbout
.DoesNotUnderstandError
: add an additional argument, allowing the keyword pairs to be passed through.doesNotUnderstandAbout
to call the sclang methoddoesNotUnderstandAbout
by wrapping the non-keyword arguments and keyword arguments in separate arrays, if there are no keywords, passnil
.When the user invokes does-not-understand behaviour, e.g.,
12.someMethodNotUnderstandByInts(1, 2, foo: 3)
,either the old
doesNotUnderstand
ordoesNotUnderstandAbout
are called, depending on which is further down in the class hierarchy.This means the most concrete class has all the control, following the rules of method overriding which users expect.
Here is an example.
2 ClassLib: Replace doesNotUnderstand with doesNotUnderstandAbout.
Implement
performWithKeys
,valueWithKeys
, andfunctionPerformWithKeys
which allow an easy way toperform
/value
/functionPerform
with both keyword arguments and normal arguments.This also centralises the error checking in
Method
/FunctionDef
so that you can't call a method with overlapping arguments and keyword-arguments.In future, the methods in
Method
andFunctionDef
could be turned into primitives.Update almost all calls to
doesNotUnderstand
with calls todoesNotUnderstandAbout
.Types of changes
To-do list