You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Probably a crazy idea, but the []= operator is very useful because it allows a parameterized setter. It only has one parameter, though.
So, what if you could declare parameterized setters with any parameter list.
classCanvas {
ColorpixelColor(int x, int y) => grid[x][y];
pixelColor(int x, int) set (Color color) {
grid[x][y] = color;
}
}
This is a parameterized setter.
It's invoked like canvas.pixelColor(3, 4) = .red;
As a setter, its return value is always void, which you can write or not.
Its member name is pixelColor= and its function type is void Function(Color, int, int), when it matters for overriding. (The value goes first as a required parameter, to allow subclasses to override with a function type that has more optional parameters. Alternatively, the function type could be the curried void Function(Color) Function(int, int). That could actually be easier to reason about, but a noSuchMethod invocation would flatten the arguments in the parameter list, so might as well be consistent. And, annoyingly, an Invocation for an invalid operator[]= has the arguments in the opposite order.)
You still cannot tear off a setter. Doing canvas.pixelColor will give you the function. If there was no such function, you still didn't write the name pixelColor=, so you won't get the setter. (So it doesn't matter whether it's function type is curried or not, it never exists less than fully applied. Except perhaps for dart:mirrors, which long ago stopped supporting new features.)
The parameterized setter can be generic too, all parameters can refer to the type parameters.
If the same scope has a normal function with compatible parameters, you can do composite assignments: canvas.pixelColor(3, 4) *= dimming ; behaves like canvas.pixelColor(3, 4) = canvas.pixelColor(3, 4) * dimming;, which must then be valid invocations of both (modulo the usual "only evaluated once" clauses). Just like operator[].
Alternatively, maybe it could extend the current setter syntax, and look "less weird" with the two parameter lists in one declaration:
classCanvas {
setpixelColor(Color color, int x, int y) {
grid[x][y] = color;
}
}
which is like a setter, only with more parameters. There has to be at least one required positional parameter for the value.
If there is only one parameter, it's just a normal setter, the dual of a getter.
If you have more than that one parameter, the rest become the parameters of a parameterized setter,
which must be invoked using (...)=, the dual of a function call.
(So set pixelColor(Color color, [int x = 0, int y = 0]) { ... } can be invoked as .pixelColor() = .red, but not .pixelColor = red. Even if all the parameters are optional, it's not an unparameterized setter, it's a parameterized setter which can be called with zero arguments.)
That said, I think the "two parameter lists" syntax is more readable, because it looks more like how it's invoked.
Related issues
Allow multiple arguments to index operators #2534 "Allow multiple arguments to index operators": Allows more parameters for one setter, operator[] .
Is a little hampered by the parameter order (value last). In the thread I suggested
Toperator[int x, int y] => ...
voidoperator [int x, int y]=(T value) => ...
as alternative. Looks more like how it's invoked, so actually a nice syntax. Maybe we could allow you to write operator[]=(index, value) as operator[](index) set (value) ,
which can then generalize cleanly to multiple parameters if we want it.)
I've suggested (can't find the issue right now) a combined getter/setter syntax to avoid
repeating the name and type (and wondering where to write a comment), like
int _foo =0;
/// The foo. If changed, will notify of changes.int foo {
get=> _foo;
set(value) { if (value != _foo) _notifyChange(_foo = value); }
}
Probably a crazy idea, but the
[]=operator is very useful because it allows a parameterized setter. It only has one parameter, though.So, what if you could declare parameterized setters with any parameter list.
This is a parameterized setter.
It's invoked like
canvas.pixelColor(3, 4) = .red;As a setter, its return value is always
void, which you can write or not.Its member name is
pixelColor=and its function type isvoid Function(Color, int, int), when it matters for overriding. (The value goes first as a required parameter, to allow subclasses to override with a function type that has more optional parameters. Alternatively, the function type could be the curriedvoid Function(Color) Function(int, int). That could actually be easier to reason about, but anoSuchMethodinvocation would flatten the arguments in the parameter list, so might as well be consistent. And, annoyingly, anInvocationfor an invalidoperator[]=has the arguments in the opposite order.)You still cannot tear off a setter. Doing
canvas.pixelColorwill give you the function. If there was no such function, you still didn't write the namepixelColor=, so you won't get the setter. (So it doesn't matter whether it's function type is curried or not, it never exists less than fully applied. Except perhaps fordart:mirrors, which long ago stopped supporting new features.)The parameterized setter can be generic too, all parameters can refer to the type parameters.
If the same scope has a normal function with compatible parameters, you can do composite assignments:
canvas.pixelColor(3, 4) *= dimming ;behaves likecanvas.pixelColor(3, 4) = canvas.pixelColor(3, 4) * dimming;, which must then be valid invocations of both (modulo the usual "only evaluated once" clauses). Just likeoperator[].Alternatively, maybe it could extend the current setter syntax, and look "less weird" with the two parameter lists in one declaration:
which is like a setter, only with more parameters. There has to be at least one required positional parameter for the value.
If there is only one parameter, it's just a normal setter, the dual of a getter.
If you have more than that one parameter, the rest become the parameters of a parameterized setter,
which must be invoked using
(...)=, the dual of a function call.(So
set pixelColor(Color color, [int x = 0, int y = 0]) { ... }can be invoked as.pixelColor() = .red, but not.pixelColor = red. Even if all the parameters are optional, it's not an unparameterized setter, it's a parameterized setter which can be called with zero arguments.)That said, I think the "two parameter lists" syntax is more readable, because it looks more like how it's invoked.
Related issues
operator[].Is a little hampered by the parameter order (value last). In the thread I suggested
Maybe we could allow you to write
operator[]=(index, value)asoperator[](index) set (value),which can then generalize cleanly to multiple parameters if we want it.)
repeating the name and type (and wondering where to write a comment), like