Open hydro63 opened 2 months ago
Sounds like a duplicate of: https://github.com/dart-lang/language/issues/1014 . Or at least strongly related to it.
I don't believe Day has ever had varargs, the closest were an attempt to design them, which didn't pan out.
Specification-wise, it's not particularly hard.
Positional parameters can end with […, ... List<int> args]
, which is a spread pattern.
If we can use patterns for parameters otherwise, then a record spread pattern ...(int x, int y) && var point
would match the next two positional arguments, and a spread list pattern would fit right in, but only for trailing positional parameters.
It would also match spread arguments like ... point
or ... list
.
A spread map pattern is more controversial, since parameters are not named with strings, and capturing argument names as symbols of a little too close to reflection. We also don't want to capture using a record of statically unknown type. So maybe there just shouldn't be unbounded named parameters.
@lrhn I don't see a reason to enable spreading some collection to bind multiple different arguments. The reason i allowed only varargs
parameter, and no other combination of the different types, is because i know that people will try to get fancy with this otherwise, and wil create ambiguous code.
As i said, in current Dart there is no other valid use-case for varargs
other than passing multiple constant parameters without having to wrap them in []
.
Also, the reason i thought there was a varargs
parameters is this: https://groups.google.com/a/dartlang.org/g/misc/c/0cQZpHs5D-g/m/NrKOuizLnqEJ
Just adding [... List<Foo> foos]
is an option. It will count as an optional positional parameter, in that it cannot be used together with named parameters. The [
...]
wrapper is there to show that.
If we have var-arg parameters, we also want var-arg arguments. That means a call like doFoo(42, "a", Foo(0), ...foos)
.
We can stop there. It is a well-defined self-consistent feature.
The reason I want to combine it with parameter patters is that it looks like one. Or more precisely, it looks similar to the record spread argument/record spread paramters that I would want together with pattern parameters.
Plain pattern parameters are just patterns on each position: void foo(int x, String y, [Foo first, Foo second, ... List<Foo> rest])
. "Spread" patterns would match multiple arguments: Box box(...(double x, double y) min, ...(double x, double y) max) => Box(bottomLeft: min, topRight: max);
which takes four arguments, but match them into records already in the parameter list, and you can call it as box(...minPoint, ...maxPoint)
to spread the records into the argument list.
That's what ... List<int> varargs
does too, takes (the rest of) arguments and combine them into a single list during the "binding actuals to formals" step of invoking the function.
So it would make sense to combine the features into one, rather than designing them separately, and risking small differences in behavior when combining the two features.
(When @munificent says that we had var-args in Dart for a while, it wasn't a feature that was ever released in a stable version. It was being worked on, but it didn't ship, for the reasons he gave. Looking from the inside, they were in Dart for a while, but from the outside, that wasn't visible.)
Actually, i think that the feature i want (varargs
where there are multiple constant parameters) could be done by using macros.
// before
@Varargs(int, "values")
int sum(){
return values.fold(0, (a,b) => a+b);
}
// after applying the macro - creates optional variables, and wraps them in list
int sum(int values_1, [int? values_2, int? values_3, ..., int? values_n]){
final values = [values_1, values_2, values_3, ..., values_n].where((e) => e != null).toList<int>();
return values.fold(0, (a,b) => a+b);
}
I don't know if it's a good reason to stop the discussion about supporting varargs
, just putting it out there.
I tried to write the macro, and found out that you can't change the function signature with macros (meaning no new arguments). That is a correct decision in the grand scheme of things, but in this case it's actually needed and also reasonable to change the signature.
Oh well, the macros solution doesn't work.
I'd argue that that particular example is better served with a collection extension:
extension IterableSum on Iterable<int> {
int sum() => fold(0, (a, b) => a + b);
}
which, afaik, already exists in the collection
package (could be wrong)
That said, i think I'd rather get a tangential-yet-similar feature of receiving any set of parameters in the shape of a record to be passed into the "actual" function, wrapper style.
One might argue "macros!" but no, because suddenly I'd have a million subtypes that would have been perfectly represented via generics.
Record spread + record args (though optional positional, optional named, and defaults don't have representation...) would give much more power to generic functions.
There's probably already an issue for that, but I otherwise don't see the point of varargs, when an optional list fits that fine. its 2 extra characters, and you get all collection features, like collection-for and collection-if, etc.
Theres also no way to define a generic-extends-function that declares a desired return type while letting the user determine the parameter shape. (perhaps <R extends Record, F extends ReturnType Function(...R)>
? idk.) that also might have an issue somewhere, but idk.
I would like to reintroduce
rest
/varargs
parameter (...rest
) in functions, while also avoiding the problems that caused it to be removed.I know that Dart use to have a
varargs
parameter, but it was removed because of the ambiguity between positional, optional, and named parameters. Those made it complex to implement and introduced ambiguity, about what value is binded to which argument.The solution to these problems, is IMO limiting where can
varargs
parameters appear, making it impossible for a user to write ambiguous and still valid code. Thevarargs
parameter would only be able to appear alone, not in combination with required, optional, or named parameters.Argumentation
The reason i would like to reintroduce
varargs
parameters, is because sometimes we need to pass a collection of parameters to the function, but wrapping the parameters inList<T>
is tedious and boiler heavy.Also, introducing
varargs
in limited capacity would not impede on the current syntax, neither will limiting it's implementation limit it's use-cases. The only possible use-case ofvarargs
parameters in current Dart is to pass a collection of constant parameters, without needing to constantly wrap them in lists. Thevarargs
parameter used to have a use-case of passing optional parameters to a function, but that has already been supplanted by optional parameters.As such,
varargs
parameter would only be able to appear as a single parameter, with no required, optional or named parameters. There would be no ambiguity in what values bind to which argument, because there would be only one sink. The compiler would also raise error when the developer tried to pass it a value of wrong type. The passed values would be collected and wrapped into aList<T>
and given to the function.Use-cases
The new
varargs
parameter could be used anywhere, where we only need to pass multiple values of the same type, but where it's expected that the developer is gonna pass them by one by one. Coincidentally, we can also find a use-case for it in Dart core libraryWhile I understand the reasons for the removal of the
varargs
parameter from Dart, since they caused a lot of problems in combination with the other types of parameters, i also think that they still have a use-case that the other parameters can't supplant.PS > it should also be possible to create the same behaviour with macros (the same as the Dart core example), but it would create a lot of boilerplate code, that's not needed