-
Notifications
You must be signed in to change notification settings - Fork 55
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
Allow spread operator in list constructors #52
Comments
If we put this into a tuple constructor the length of the tuple would have to be computed at runtime. You had proposed a terminology change to tuple constructor to be referred to as fixed length constructor .. would It works for arg-list because the resulting value is received in the function as a rest arg. However, the current draft has this sentence "The rest-arg is not restricted to supplying the part of the argument list that will be bound to a rest-param" which I don't grok .. and I looked at the rest-param definition and its not clear how that would work. |
Given that we are saying that a tuple constructor (or whatever we decide to rename it to) creates a fixed length value, then As regards your second para, functions don't receive a rest arg as a distinct thing: they receive a single list with all their args (although it is not reified as a list); this list is not necessarily fixed length. (This is described in the function types section.) With One result of all this is that
|
Ack and +1 to allowing To call bar() I'd have to say:
as bar() takes a single arg of a tuple type. So this is not a very useful example but in general being able to take a tuple value and spread it into an arg list of a function is very nice. What this means is that param-list of a function is really a way to declare a tuple giving names to the slots so one can access them differently from indices. It feels to me like we should generalize that to all tuples .. |
On your last para, a tuple-binding-pattern effectively does that. You can think of the parameter list of a function as doing a destructuring assignment on the argument list. |
We should also allow spread in mapping constructors applied to mapping values, e.g.
This is very useful if you want to make use of something that you have matched in a binding pattern with |
I'm working on the implementation. Consider the following scenario. string[] a1 = ["s"];
[string, string, string...] a2 = [...a1, ...a1]; // @output ["s","s"]
[(int|string), string, string...] a3 = [...a1, ...a1]; // @error no filler value for type '(int|string)'
[string, int...] a4 = [...a1]; // @error incompatible types: expected 'int', found 'string' Is the above the expected behavior? Or should |
I agree I hadn't considered how spread interacts with filler values, but I think you are right to take them into account. So then
Right? That seems reasonable. |
Wouldn't allowing this mean that checking and adding the unspecified (fill-able) members is a responsibility of the runtime list value creation, rather than something the compiler can desugar? Because we don't know at compile-time if we have to use filler values and/or for which members we have to use filler values? jBallerina actually checks the length and fills values at runtime atm, but before this change I was thinking it could probably be handled at compile-time. |
type checking gets more complicated for a scenario like this, X[] b = [];
[Y, Z...] f = [y];
[P, Q, R, S, T, U...] a = [
...b,
c, // should be compatible with types P, Q, R, S, T, and U
d, // should be compatible with types Q, R, S, T, and U
e // should be compatible with types R, S, T, and U
...f,
g, // should be compatible with types T and U
h // should be compatible with types U
]; is it worth implementing? Maybe we better off saying, varying length lists with spread operator are allowed only for the rest-descriptor, considering #52 (comment) as well. |
That's more restrictive than for |
On further thought, I suggest: spread operator with varying-length is allowed in two cases:
|
I think we can relax allowing spread operator with varying length for inherent types having required members a little further. As long as we can guarantee that the required members are fulfilled, we can allow as many as spread members with varying lengths. int[] a1 = [];
[int, string, any...] a2 = [1, "s", ...a1, ...a1, 23, "x", ..a1];
[int, string] a3 = [1, "s"];
[int, any, anydata...] a4 = [...a3, ...a1, true, ...a1, "x"];
[int, string, string] a5 = [1, "s"];
[int, any, anydata...] a6 = [...a5, ...a1, true, ...a1, "x"]; In the above scenarios, required members are guaranteed to have a value. In a nutshell, we do the last member restriction, only when the required members are not guaranteed to have a value. [int, string] a7 = [1, "s"];
[int, any, int, anydata...] a8 = [...a7, ...a1]; // Here only the last member is allowed to be having a varying length |
So I suggest: the spread operator with varying-length is limited to last member only if the inherent type of the list being constructed has required members that are not guaranteed to have a value. Allowed anywhere otherwise. |
I would like to address Maryam's point too. |
Then I think we have to disallow the following.
So in summary: |
I think that's OK. We need to make "guaranteed to have a value" precise, so I would phrase it something like:
|
Should also make this work for table constructors. Edited: this is #1102 |
We need to also finalize the limitations with the spread operator when there is no contextually expected type for the list. As per the current implementation with (int|string)[] x = [1, 2];
var _ = [0, ...x]; // not allowed atm.
["s", int...] y = ["s", 2];
var _ = [0, ...y]; // not allowed atm. We can infer the type for the above as a tuple type having a rest descriptor; I suggest the limitation as follows, |
I don't see why there should be limitations. The issue is what type you infer for the constructed list, when you have a varying length spread member that is not the last member of the list constructor. The obvious answer is that this member and all subsequent members turn into a T... where T is the union of all the possible member types. |
JavaScript allows spread operator
...
not only in function calls but also in list constructors. We should allow this also. It also allows the spread operator to refer to anything iterable, not just a list.At the moment it when applied to a list, it has the effect of specifying each member as a positional argument; I wonder if it could also be made to apply to a record with the effect of specifying each member as a named argument.
JavaScript also allows multiple occurrences of the spread operator in a single argument list.
The text was updated successfully, but these errors were encountered: