-
-
Notifications
You must be signed in to change notification settings - Fork 97
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
Add support for variadic functions (varargs) to GDScript #1034
Comments
See also godotengine/godot#16565.
You should fill in those fields as well 🙂 In the meantime, you can use this workaround. Not pretty, but it does the job with up to 9 arguments: # Note that arguments explicitly passed as `null` will be ignored by this function.
func some_function(arg1 = null, arg2 = null, arg3 = null, arg4 = null, arg5 = null, arg6 = null, arg7 = null, arg8 = null, arg9 = null):
var array = []
for argument in [arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9]:
if argument != null:
array.push_back(argument)
# Do stuff with `array`. |
I don't really understand this question haha because everything would be better to be core than an asset, wouldn't it, or do I understand nothing for that matter?? |
array pack and unpack for arguments would be great, similar to Javascript / Node / PHP: <?php
function example(...$items)
{
foreach($items as $item){
print_r($item);
}
}
// Calling the function:
example(... ["a", "b", "c"]);
// or
example("a", "b", "c"); I think this would be more intuitive and you can name the packed argument however you want. On GDScript my syntax proposal is this similar: func example(...items):
for item in items:
print(item)
# Calling the function:
example(... ["a", "b", "c"])
# or
example("a", "b", "c") IMHO using Triple dot |
I don't know if it would be harder to implement **kwargs if this exponentiation operator ** would be added too. What do you think? I hope they won't interrupt each other if both of them will get part of gdscript. |
Thumps up on this one, "splat" arguments make designing clean APIs much nicer, and allows for better metaprogramming, (Ie, "Get all thse parameters, do something funky with them and then pass them to this function", opening up various functional (ie partial applications, currying, etc) and OOP methodologies (think of how python can pass on *args ,**kwargs to ancestors). Its a supremely useful construct. Re: core vs addons. Not sure how you implement language features as plugins lol. |
this would be a useful feature |
I've come to the point in my current project where varargs would greatly improve code structure and readability. I'd prefer the '...' over use of splats to avoid confusion as mentioned in previous comments. |
I would like to point out that this would greatly help some builtin components of Godot. For example the bool interpolate_callback(object: Object, duration: float, callback: String, arg1: Variant = null, arg2: Variant = null, arg3: Variant = null, arg4: Variant = null, arg5: Variant = null) With the following description:
This limits the amount of individual arguments to 5 for the callback, and bloats the signature of With variadic arguments, the method signature could be reduced to bool interpolate_callback(object: Object, duration: float, callback: String, ...args)
# Or:
bool interpolate_callback(object: Object, duration: float, callback: String, ...args: Variant[]) And allow for more than 5 arguments to the callback. |
@Speedphoenix That said, Tween is being rewritten to have less methods that take lots of parameters: godotengine/godot#41794 |
That looks great. The |
…ents#emit`. New emit method to be removed when [support for varargs](godotengine/godot-proposals#1034) lands. Signed-off-by: Ross Hadden <[email protected]>
Maybe this is a silly question, but how is: my_func(arg0, arg1, arg2) Any better than: my_func([arg0, arg1, arg2]) It seems to me like it only saves writing 2 characters in exchange for making GDScript more complicated. |
@LightningAA Well, typing (as in writing code) aside the main benefit would be parameter validation. An array is just and array, but multiple distinct parameters, even in a variadic function, can be validated at a signature level. This may be less important if you don't rely on the typing system. |
@pycbouh But what about typed arrays that were added to GDScript 2.0? |
I guess they can help. But as I've said, that's a reason aside from writing code. But coding is also important. If a variadic function has some arguments before the varargs, it may be nicer to write them seamlessly instead of consciously breaking off a set of arguments into an array: my_func(param0, param1, vararg0, vararg1) vs my_func(param0, param1, [ vararg0, vararg1 ]) That's a benefit for the user of the function, and the developer of the function can still write sensible code by using some sort of |
Typed arrays only work when all items are of one type. Functions arguments usually have different types |
Yeah, but varargs would either be of the same type or of a generic type like |
I'm still not completely sold, my_func(param0, param1, [vararg0, vararg1]) Looks fine to me. I wouldn't really care if varargs were added, especially if they weren't that complicated to implement, but it feels to me like adding unnecessary complexity, "There should be one-- and preferably only one --obvious way to [pass a variable amount of arguments to your function]." |
I feel like this is putting that idea to extreme. You can argue that you don't need multiple arguments at all, just pass everything as an array. One way and all. Variadic functions do not serve the same purpose as passing an array as a parameter. Passing an array is a generic operation, and variadic function tells something very specific to the user about the operation. I don't have a good example handy, I'm afraid, but that's the point of having specific syntax — to pass on additional context. I agree that maybe typed arrays may be a middle ground though. |
Looking at this topic almost exactly a year later, I do not agree anymore with myself concerning the syntax, because I learned Java and got to know how it is made there with the three dots, it does even have much more sense than a star! |
Yes, but then all your arguments would have to be the same type or any type, and there'd be no assigning names to different parameters, and the amount of parameters would be hard to enforce at compile time. varargs is already pretty much already an array, and all it does is save the user typing 2 characters.
That's a better argument, but I'd still like to hear some examples 😄 |
I think the python syntax should be used as that's what gdscript draws most from.
Most of the default values are fine, but you may want to change a few in the middle. In Godot, you'd either have to pass a dict and check and fill default values manually in the method, or enter in every argument's default value until you reach the one you want to change when calling. This is quite cumbersome.
it doesn't make a huge difference like kwargs, but it does make the code a little more readable. |
I'm not a python expert, but that sounds more like named arguements (#902), not **kwargs (which I understand to be like *args but it's a dictionary instead of an array). |
You are absolutely correct. My mistake. In addition, my point on flags would be better implemented with bit masks. |
Workaround for |
I'm trying to do something like below, which would be a lot easier if I could override
Language elements like this are useful for reflection which can improve developer usage. EDIT:
EDIT2: |
This is a useful addition to 4.0 and the new GDScript but still you can't create a callable from anything in @globalscope or @GDscript |
I was instructed to post my solution here. My proposal is to add a locally scoped Note that def add():
sum = 0
for x in parameters: # any passed parameters automatically get collected in locally scoped parameters property
sum += x
return sum
def _ready():
print( add(1,2,3,4,5,6,7,8) ) # parameters for this function call would be [1,2,3,4,5,6,7,8]
print( add(1,2,3) ) # parameters for this function call would be [1,2,3] Named parameters would reduce the amount of values in def print_params(a,b):
for x in parameters:
print(x)
def _ready(a,b):
print_params(1,2,3,4,5,6,7,8) # a = 1 b = 2 parameters = [3,4,5,6,7,8]
print_params(1,2,3) # a = 1 b = 2 parameters = [3] |
I like the idea of having an "automatic" variable to hold the variable parameters. When trying to described a couple tweaks to the idea I realized it is not clear (indicative) that a method accepts variable parameters. Adding support for the ellipses (...) notation in the method signature clearly indicates the method that accepts a variable number of parameters and should make auto-completion much easier. It would also be nice if the automatic variable containing the varargs has a name that is otherwise not allowed for defined variables (@@VarArgs, ~~varargs, $$args?) and is easy for the parser to recognize without confusion. This assures there is no accidental collision with developer defined variable names. |
In order to be more formal about varargs, maybe an Array in last position could be seen as varargs in a call by GDScript ? Example : func myFun(a:int,others:Array): myFun(1,"b",2,Vector.ZERO) is understood then as myFun(1, ["b",2,Vector.ZERO) People could argue they want to keep arguments control of analyser doing its job, @ Varargs |
func myFun(a:int, ...others): => would implies others is Array, that seems good too and safer that annotation I proposed, because it is on argument itself I think it has to be reserved to last parameter to be more simple to parse and check. |
Making an argument type conditionally collect all remaining args would likely be more prone to causing issues. I'm personally +1 for the spread operator to "collect" the args beyond that point, as is common in other language implementations. If the language can imperatively interpret an array type as collecting the remaining args, then it can interpret a spread or other operator to declaratively do it instead. That said, there's some merit to that suggestion also. If it works out simpler in implementation, the last arg could just as well be typed |
Today I would also need this, and found this discussion. I lost a bit between the different suggestion.
It would look like this: func print_sum(of_what: String, values: int...):
var sum := 0
for value in values:
sum += value
print("%s = %d" % [of_what, sum]) So its just an array. Built-in Array supports all Variant types, so can be translated. var already_spent_money: int = get_it_from_somewhere()
var open_planned_costs: int = get_it_from_elsewhere()
print_sum("foreseen overall cost", already_spent_money, open_planned_costs) But also like this: var planned_cost_item: Array[int] = get_hundreds_of_int_from_eg_a_database()
print_sum("overall opened plan cost", planned_cost_items) Could work without type hints: func print_sum(of_what, values...):
... And with default values: func print_sum(of_what: String, values: int... = []):
... If "..." does not look nice, with a varargs keyword its the same. An as I remember, if zero number of varargs is given by the caller, the function simply gets an empty array (so not null, no need for null check inside the function). I cannot decide now, which one is more useful inside the function. |
Ahh OK sorry, I see "..." was already in discussion |
This comment was marked as off-topic.
This comment was marked as off-topic.
If someone wants to implement this they're free to do so, no decision needs to be made before doing so, and someone showing an implementation improves the chances of it being accepted, some of the core developers that have done large work on GDScript can take it on if they are interested and have ideas, but features are added when someone figures out how to implement them, and then approved, it's not a process of "okay, this is good, now we'll tell someone to go do it". |
Any good solution for this example?
was hoping for:
Tried to find a sugared solution but couldn't figure it out. |
Any progress on this? Time and time again I run into the issue of needing varidic functions on my projects, at this point I might just make my own GDExtension to remedy the problem. |
@vvvvvvitor See godotengine/godot#82808. This implements the first part of the proposal, rest parameter, which allows you to declare variadic functions (i.e. declare a parameter that packs extra arguments into an array). This part is quite small, I think it is completely ready. However, many also expect the second part, spread syntax, which allows you to unpack arrays into argument/element lists. While this is essentially just syntactic sugar for In any case, this will not be included in 4.3 due to the feature freeze, neither the first nor the second part. As for 4.4, it depends on many factors (will the spread syntax part be ready, will other contributors have time to test and review it). |
At least for now you could pass the additional method parameters as an array and call bindv on method: func call_method(method, args = []):
...
rpc(method.bindv(args))
...
call_method(print, ["hello"])
or
func call_method(method): ...
call_method(print.bind("hello"))
call_method(print.bindv(["hello"]))
call_method(func(): print("hello")) |
Describe the project you are working on:
Reaction game
Describe the problem or limitation you are having in your project:
I can't call a function with a variable amount of arguments (if they don't have a default)!
Describe the feature / enhancement and how it helps to overcome the problem or limitation:
I could call a function with a variable amount of arguments. Of course, you should also be able to pass the argument you want, not necessarily in the given order but this is something different: look here
Describe how your proposal will work, with code, pseudocode, mockups, and/or diagrams:
If this enhancement will not be used often, can it be worked around with a few lines of script?:
No
Is there a reason why this should be core and not an add-on in the asset library?:
Yes, it is useful for every project.
Bugsquad edit (keywords for easier searching): python, args, kwargs
The text was updated successfully, but these errors were encountered: