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

Implement doesNotUnderstandWithKeys #6227

Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
318f139
First commit.
Mar 2, 2024
591e8f6
Format
Mar 2, 2024
b30607d
Format again
Mar 2, 2024
4e77318
Format again again
Mar 2, 2024
b336225
Provide better interface for redirecting function calls
Mar 3, 2024
389c8d3
Comment and refactor code
Mar 4, 2024
958af68
Documentation and small renaming of arg
Mar 4, 2024
be80f55
Remove helper methods from object, and delegate to Object, Function, …
Mar 9, 2024
ec776bc
Update Tuning*doesNotUnderstandWithKeys
Mar 9, 2024
a8fafc1
Renaming and formatting.
Mar 10, 2024
2154969
Remove evaluateWith* methods, simplify the argument creation, update …
Mar 11, 2024
faf6b29
Formatting.
Mar 11, 2024
6d6a86f
Simplify how callable arguments are made.
Mar 12, 2024
d9f07f2
Implement performWith and valueWith
Mar 13, 2024
750f8f5
Remove reformatting.
Mar 13, 2024
61b2c68
Remove reformatting.
Mar 13, 2024
4144747
Move the creation of the argument array to FunctionDef so it can be u…
Mar 13, 2024
32c3333
Update Scale and Tuning
Mar 13, 2024
919956f
Given Object a default value with and fix commit syn issue.
Mar 13, 2024
9dc1da5
Remove VarArgs on performWith
Mar 13, 2024
26428b3
Missed bracket from commit
Mar 13, 2024
aa519f4
Simply Scale's doesNotUnderstandWithKeys
Mar 13, 2024
ab00109
Rename defaultArguments to defaultArgs
Mar 13, 2024
ad04f69
Docs and a comment
Mar 13, 2024
aeb6a95
Docs remove old method
Mar 13, 2024
b3f7a7f
Simply some logic in makePerformableArray
Mar 13, 2024
ebf5805
Actually undo mistaken formatting. Fixed comments. Simplify code.
Mar 14, 2024
f79e656
Depreciate functionPerformList in favour of functionPerformWith
Mar 14, 2024
27679c5
Docs
Mar 14, 2024
704d0cf
Remove indents
Mar 14, 2024
d15afc8
Comment grammar
Mar 14, 2024
76b7708
Reinstate functionPerformList
Mar 14, 2024
024eac1
Reinstate functionPerformList
Mar 14, 2024
0b15f0f
Reinstate functionPerformList
Mar 14, 2024
b38cb22
Reinstate functionPerformList
Mar 14, 2024
ad8f2cb
Formatting, small optimization.
Mar 14, 2024
629686c
Remove default args of valueWith
Mar 17, 2024
d0b8df2
Tidy, rename, lots of docs.
Mar 20, 2024
7826824
Docs parens, undo sc formatting.
Mar 20, 2024
ef4fdc3
Remove semi-colons, Docs typo
Mar 21, 2024
bf55260
Typo and spaces/tabs
Apr 15, 2024
94a6cd2
Typo and spaces/tabs
Apr 15, 2024
5e4a7f8
Undo auto indent.
Apr 15, 2024
eda146e
Remove DoesNotUnderstandWithKeysError
Apr 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 12 additions & 5 deletions HelpSource/Classes/Function.schelp
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,15 @@ e = Environment.make({ ~a = 3; ~b = 10 });
method::valueWith
Evaluate the function using a mix of arguments and keyword arguments.

Errors are thrown if: the arguments collide, an unknown keyword is found, or there are too many arguments.
See link::Classes/FunctionDef#-makePerformableArray:: for a detailed description.

argument::argumentsArray
Arguments as an link::Classes/Array::.
Arguments as an array.

argument::keywordArgumentPairs
Keyword arguments as any link::Reference/Key-Value-Pairs:: structure.

argument::keywordArgumentEnvir
Keyword arguments as an link::Classes/IdentityDictionary::.

discussion::
code::
Expand Down Expand Up @@ -164,14 +168,17 @@ such as in the object prototyping implementation of Environment.

Ensure both this method and link::#-functionPerformList:: do the same thing or the behaviour will change when the user passes keyword arguments.

Errors are thrown if: the arguments collide, an unknown keyword is found, or there are too many arguments.
See link::Classes/Method#-makePerformableArray:: or link::Classes/FunctionDef#-makePerformableArray:: for a detailed description.

argument::selector
The message to be run as a link::Classes/Symbol::.

argument::argumentsArray
Arguments as an link::Classes/Array::.

argument::keywordArgumentEnvir
Keyword arguments as an link::Classes/IdentityDictionary::.
argument::keywordArgumentPairs
Keyword arguments as any link::Reference/Key-Value-Pairs:: structure.

method::performWithEnvir

Expand Down
54 changes: 28 additions & 26 deletions HelpSource/Classes/FunctionDef.schelp
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,11 @@ code::
::

method::argumentNamesForCall
Returns the argument names in code,
both for functions and methods (in methods, link::#-argNames:: have this as first item).
Returns the argument names in as needed for calling the function as an link::Classes/Array::.
JordanHendersonMusic marked this conversation as resolved.
Show resolved Hide resolved
This will remove any internal arguments.

method::prototypeFrame

Get an array of default values for all arguments and variables.
Get an link::Classes/Array:: of default values for all arguments and variables.

code::
{ |a = 9, b = 10, c| a + b }.def.prototypeFrame;
Expand All @@ -66,7 +65,7 @@ code::
::

method::defaultArgs
Returns the default arguments needed to call the function.
Returns the default arguments supplied in place of those not specified in a function call, if no default was provided for an argument, then link::Classes/Nil:: is returned in its place.


method::varNames
Expand Down Expand Up @@ -123,49 +122,52 @@ code::
::

method::makePerformableArray
Returns an array that can be passed to link::Classes/Object#-perform:: or link::Classes/Function#-value::.

This is used internally by link::Classes/Function#-valueWith:: which is sufficient for all uses and recommended over link::#-makePerformableArray::.

Returns an array that can be passed directly to link::Classes/Function#-valueArray::.

Both keyword and non-keyword arguments may be provided at the same time.
If a value for an argument is provided in both the keyword and non-keyword arguments this will throw an error.
If a keyword argument is provide, but the name can't be found an error will be thrown.

Errors are thrown if: a value for an argument is present in both the code::argumentsArray:: and the code::keywordArgumentPairs:: (argument collision),
if a keyword argument's name cannot be found (unknown keyword),
and if too many arguments are given to code::argumentsArray:: (too many arguments).

Variable arguments are best placed with a keyword argument.
If all the non-keyword arguments are present
(and the keywords empty),
then any remaining arguments will be passed to the function's variable argument if present,
if not, it will throw.
However, if the code::keywordArgumentPairs:: is empty and all arguments in the code::argumentsArray:: are present, any extra arguments in code::argumentsArray:: will be passed to the variable argument, if present.
Please note, that variable arguments are unpacked and individually appened to the end of the array, not contained in their own array.

This method can be useful when implementing link::Classes/Object#-doesNotUnderstand:: and link::Classes/Object#-doesNotUnderstandWithKeys::,
as it is the easiest way to call something with a mixture of arguments and keyword arguments.

argument::argumentsArray
Arguments as an array.
Arguments as an link::Classes/Array::.

argument::keywordArgumentEnvir
Keyword arguments in an link::Classes/IdentityDictionary::.
argument::keywordArgumentPairs
Keyword arguments as any link::Reference/Key-Value-Pairs:: structure.

returns::Arguments as an array.
Variable arguments are appended onto the end of the array.
returns::Arguments as an link::Classes/Array::.

JordanHendersonMusic marked this conversation as resolved.
Show resolved Hide resolved
discussion::
code::

(
f = { |a b ...e|
var i = 0;
a.debug(\a);
b.debug(\b);
e.debug(\e);
};
)

f.value(
*f.def.makePerformableArray([\a], (e: [1,2,3]))
);
// using makePerformableArray, not recommended
(
var args = f.def.makePerformableArray([\a], (e: [1,2,3]));
f.valueArray(args);
)

::
Better in this case to use link::Classes/Function#-valueWith:: directly as it calls this internally.
code::
// using valueWith, recommended
f.valueWith([\a], (e: [1,2,3]));
::


subsection::Generating Strings

method::makeFuncModifierString
Expand Down
62 changes: 55 additions & 7 deletions HelpSource/Classes/Method.schelp
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,15 @@ instanceMethods::

method::ownerClass
returns::
The link::Classes/Class:: for which the method is part of the implementation.
If this is a class method, then the owner class will be a 'Meta_Class'.
If the receiver is an instance method, the owner class is the class for this instance.
For a class method, it is the class of the class, whose name is prefixed with "Meta_".

code::
m = Array.findRespondingMethodFor('reverse');
m.ownerClass; // Array
m = Array.class.findRespondingMethodFor(\new);
m.ownerClass; // Meta_Array
::

method::name
returns::
Expand All @@ -54,14 +61,55 @@ returns::
A link::Classes/Symbol:: which contains the name of the primitive function that implements the Method,
if there is one, otherwise returns link::Classes/Nil::.

method::defaultArgs
Returns the default arguments needed to call the function.
This will correctly omit 'this'.

method::argumentNamesForCall
Same as link::#-argName:: but will omit 'this', useful when calling the method.
Returns the argument names in as needed for calling the function as an link::Classes/Array::.
JordanHendersonMusic marked this conversation as resolved.
Show resolved Hide resolved
This will remove any internal arguments include 'this'.

method:: filenameSymbol
returns::
A link::Classes/Symbol:: which is the full path of the source file that this method is defined in.

method::makePerformableArray
This is used internally by link::Classes/Object#-performWith:: which is sufficient for all uses and recommended over link::#-makePerformableArray::.

Returns an array that can be passed to link::Classes/Object#-performList:: along with the selector.

Both keyword and non-keyword arguments may be provided at the same time.

Errors are thrown if: a value for an argument is present in both the code::argumentsArray:: and the code::keywordArgumentPairs:: (argument collision),
if a keyword argument's name cannot be found (unknown keyword),
and if too many arguments are given to code::argumentsArray:: (too many arguments).

Variable arguments are best placed with a keyword argument.
However, if the code::keywordArgumentPairs:: is empty and all arguments in the code::argumentsArray:: are present, any extra arguments in code::argumentsArray:: will be passed to the variable argument, if present.
Please note, that variable arguments are unpacked and individually appened to the end of the array, not contained in their own array.


argument::argumentsArray
Arguments as an link::Classes/Array::.

argument::keywordArgumentPairs
Keyword arguments as any link::Reference/Key-Value-Pairs:: structure.

returns::Arguments as an link::Classes/Array::.

discussion::
code::
a = "meow MEOW woof bahh bahh Meow";

// how a user might call a method
a.findAll("meow", ignoreCase: true);


// using makePerformableArray directly, not recommended
(
var args = String
.findRespondingMethodFor('findAll')
.makePerformableArray(["meow"], [ignoreCase: true]);

a.performList('findAll', args);
)

// using performWith, recommended
a.performWith('findAll', ["meow"], [ignoreCase: true]);
::
25 changes: 18 additions & 7 deletions HelpSource/Classes/Object.schelp
Original file line number Diff line number Diff line change
Expand Up @@ -517,29 +517,40 @@ a.performKeyValuePairs(\value, [\a, 1, \b, 2, \c, 3, \d, 4]);
::

method::performWith
Evaluate a method using a mix of arguments and keyword arguments.

Errors are thrown if: the arguments collide, an unknown keyword is found, or there are too many arguments.
See link::Classes/Method#-makePerformableArray:: for a detailed description.

argument:: selector
A link::Classes/Symbol:: representing a method selector.

argument::argumentsArray
Arguments as an link::Classes/Array::.

argument::keywordArgumentEnvir
Keyword arguments as an link::Classes/IdentityDictionary::.
argument::keywordArgumentPairs
Keyword arguments as any link::Reference/Key-Value-Pairs:: structure.

discussion::
code::
7819.performWith(\asTimeString, [0.1, 365], (decimalPlaces:4));

{ SinOsc.ar }.performWith(\plot, [0.05], (minval:0, maxval:1));

"meow MEOW woof bahh bahh Meow".performWith('findAll', ["meow"], [ignoreCase: true]);

// calling value on function shows how arguments are assigned:
(
f = { |a, b ...e|
JordanHendersonMusic marked this conversation as resolved.
Show resolved Hide resolved
a.debug(\a);
b.debug(\b);
e.debug(\e);
};

f.performWith(\value, [\a], (e: [1,2,3]));
)
::
However, when calling a link::Classes/Function:: it is best to use link::Classes/Function#-valueWith::.
code::
f.valueWith([\a], (e: [1,2,3]));
::

method::tryPerform

Expand Down Expand Up @@ -705,10 +716,10 @@ Object's implementation will throw an error, but other classes (link::Classes/Id
argument::selector
The name of the message as a link::Classes/Symbol::.

argument::argsArray
argument::argumentsArray
An link::Classes/Array:: of arguments that have been passed directly and not via keywords.

argument::keywordArrayPair
argument::keywordArgumentPairs
The arguments that have been passed via keywords.
These are formatted as an link::Classes/Array:: of key-value pairs link::Reference/Key-Value-Pairs::.

JordanHendersonMusic marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
17 changes: 6 additions & 11 deletions SCClassLibrary/Common/Collections/Dictionary.sc
Original file line number Diff line number Diff line change
Expand Up @@ -560,23 +560,18 @@ IdentityDictionary : Dictionary {
^this.superPerformList(\doesNotUnderstand, selector, args);
}

doesNotUnderstandWithKeys {|selector, argsArray, keywordArgsAsPairs|
doesNotUnderstandWithKeys {|selector, argumentsArray, keywordArgumentPairs|
if(know.not){
^this.superPerformList(\doesNotUnderstandWithKeys, selector, argsArray, keywordArgsAsPairs )
^this.superPerformList(\doesNotUnderstandWithKeys, selector, argumentsArray, keywordArgumentPairs)
};

this[selector] !? {|f|
^f.functionPerformWith(\value, [this] ++ argsArray, keywordArgsAsPairs.asEvent);
^f.functionPerformWith(\value, [this] ++ argumentsArray, keywordArgumentPairs)
};

// Unlike in doesNotUnderstand, we don't convert to a setter as this isn't
// possible with keyword args.

// Note how the selector is passed in here.
// Unlike in doesNotUnderstand, we don't convert to a setter as this isn't possible with keyword args.
// Note how the selector is also passed to forward.
this[\forward] !? {|f|
^f.functionPerformWith(\value, [this, selector] ++ argsArray, keywordArgsAsPairs.asEvent)
^f.functionPerformWith(\value, [this, selector] ++ argumentsArray, keywordArgumentPairs)
};

^nil
}

Expand Down
12 changes: 6 additions & 6 deletions SCClassLibrary/Common/Collections/Scale.sc
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ Scale {

*doesNotUnderstand { |selector, args|
var scale = this.newFromKey(selector, args);
if(scale.notNil) { ^scale.deepCopy };
if(scale.notNil) { ^scale };
JordanHendersonMusic marked this conversation as resolved.
Show resolved Hide resolved
^super.doesNotUnderstand(selector, args)
}

*doesNotUnderstandWithKeys {|selector, argsArray, keywordArgsAsPairs|
^Scale.performWith(\newFromKey, [selector] ++ argsArray, keywordArgsAsPairs)
*doesNotUnderstandWithKeys {|selector, argumentsArray, keywordArgumentPairs|
^Scale.performWith(\newFromKey, [selector] ++ argumentsArray, keywordArgumentPairs)
}
JordanHendersonMusic marked this conversation as resolved.
Show resolved Hide resolved

*newFromKey { |key, tuning|
Expand Down Expand Up @@ -191,12 +191,12 @@ Tuning {

*doesNotUnderstand { |selector, args|
var tuning = this.newFromKey(selector, args);
if(tuning.notNil) { ^tuning.deepCopy };
if(tuning.notNil) { ^tuning };
JordanHendersonMusic marked this conversation as resolved.
Show resolved Hide resolved
^super.doesNotUnderstand(selector, args)
}

*doesNotUnderstandWithKeys {|selector, argsArray, keywordArgsAsPairs|
^Tuning.performWith(\newFromKey, [selector] ++ argsArray, keywordArgsAsPairs.asEvent);
*doesNotUnderstandWithKeys {|selector, argumentsArray, keywordArgumentPairs|
^Tuning.performWith(\newFromKey, [selector] ++ argumentsArray, keywordArgumentPairs)
}

*newFromKey { | key |
Expand Down
12 changes: 6 additions & 6 deletions SCClassLibrary/Common/Core/AbstractFunction.sc
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,8 @@ UnaryOpFunction : AbstractFunction {
functionPerformList { arg selector, arglist;
^this.performList(selector, arglist)
}
functionPerformWith {|selector, argumentsArray, keywordArgumentEnvir|
^this.performWith(selector, argumentsArray, keywordArgumentEnvir)
functionPerformWith {|selector, argumentsArray, keywordArgumentPairs|
^this.performWith(selector, argumentsArray, keywordArgumentPairs)
}
storeOn { arg stream;
stream <<< a << "." << selector;
Expand Down Expand Up @@ -286,8 +286,8 @@ BinaryOpFunction : AbstractFunction {
functionPerformList { |selector, arglist|
^this.performList(selector, arglist)
}
functionPerformWith {|selector, argumentsArray, keywordArgumentEnvir|
^this.performWith(selector, argumentsArray, keywordArgumentEnvir)
functionPerformWith {|selector, argumentsArray, keywordArgumentPairs|
^this.performWith(selector, argumentsArray, keywordArgumentPairs)
}
storeOn { arg stream;
stream << "(" <<< a << " " << selector.asBinOpString;
Expand Down Expand Up @@ -318,8 +318,8 @@ NAryOpFunction : AbstractFunction {
functionPerformList { |selector, arglist|
^this.performList(selector, arglist)
}
functionPerformWith {|selector, argumentsArray, keywordArgumentEnvir|
^this.performWith(selector, argumentsArray, keywordArgumentEnvir)
functionPerformWith {|selector, argumentsArray, keywordArgumentPairs|
^this.performWith(selector, argumentsArray, keywordArgumentPairs)
}
storeOn { arg stream;
stream <<< a << "." << selector << "(" <<<* arglist << ")"
Expand Down
12 changes: 6 additions & 6 deletions SCClassLibrary/Common/Core/Error.sc
Original file line number Diff line number Diff line change
Expand Up @@ -192,12 +192,12 @@ DoesNotUnderstandError : MethodError {
}

DoesNotUnderstandWithKeysError : MethodError {
var <>selector, <>argsWithOutKeys, <>argsWithKeys, <suggestedCorrection, suggestion = "";
*new { arg receiver, selector, argsWithOutKeys, argsWithKeys;
var <>selector, <>argumentsArray, <>keywordArgumentPairs, <suggestedCorrection, suggestion = "";
*new { arg receiver, selector, argumentsArray, keywordArgumentPairs;
^super.new(nil, receiver)
JordanHendersonMusic marked this conversation as resolved.
Show resolved Hide resolved
.selector_(selector)
.argsWithOutKeys_(argsWithOutKeys)
.argsWithKeys_(argsWithKeys.asEvent)
.argumentsArray_(argumentsArray)
.keywordArgumentPairs_(keywordArgumentPairs)
.init
}

Expand Down Expand Up @@ -228,9 +228,9 @@ DoesNotUnderstandWithKeysError : MethodError {
"RECEIVER:\n".post;
receiver.dump;
"ARGS WITHOUT KEYS:\n".post;
argsWithOutKeys.dumpAll;
argumentsArray.dumpAll;
"ARGS WITH KEYS:\n".post;
argsWithKeys.dumpAll;
keywordArgumentPairs.dumpAll;
this.errorPathString.post;
if(protectedBacktrace.notNil, { this.postProtectedBacktrace });
this.dumpBackTrace;
Expand Down