Skip to content

Setter functions, parameterized setters. #4677

@lrhn

Description

@lrhn

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.

class Canvas {
  Color pixelColor(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:

class Canvas {
  set pixelColor(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
    T operator[int x, int y] => ...
     void operator [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); } 
    }
    this could generalize to functions ... somehow.
    Color pixelColor(int x, int y) <keyword here?> {
        get => grid[x][y]; 
        set(color) { grid[x][y] = color; }
    }

Metadata

Metadata

Assignees

No one assigned

    Labels

    featureProposed language feature that solves one or more problems

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions