Skip to content
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 setters to declare a return type of Never #4202

Open
mateusfccp opened this issue Dec 10, 2024 · 7 comments
Open

Allow setters to declare a return type of Never #4202

mateusfccp opened this issue Dec 10, 2024 · 7 comments
Labels
feature Proposed language feature that solves one or more problems

Comments

@mateusfccp
Copy link
Contributor

mateusfccp commented Dec 10, 2024

This may be only useful on some few edge cases, so I am not sure if this is viable or not, but it may not be so hard to do and isn't breaking.

Currently, the specification explicitly states:

It is a compile-time error if a setter declares a return type other than void.

With null-safety and the Never type introduced, there are cases where one might want to change their setter return type to indicate that an error is never thrown.

For instance, UnmodifiableListView.operator []= always throw.

It could be useful to be able to declare the return type of those kinds of setters as Never.

@mateusfccp mateusfccp added the feature Proposed language feature that solves one or more problems label Dec 10, 2024
@lrhn
Copy link
Member

lrhn commented Dec 10, 2024

The return type of a setter doesn't matter, because the type isn't used anywhere in the specification.
Any returned value is lost, so it's type doesn't matter.

If we allow Never as return type, as a signal that the setter invocation never returns any value, then we'd want the spec to recognize that.
That would mean something like:

  • If an assignment expression invokes a setter (it's not an assignment to a local variable), then let R be the return type of the signature of the setter being invoked, and let S be the static type of the assigned expression. If R is a bottom type (any subtype of Never), then the static type of the assignment expression is Never. Otherwise the static type of the assignment expression is S.
  • It is be a compile time error if the declared return type of a setter is anything except void or Never. A setter declaration with no return type defaults to void (even if we can see that the cost definitely throws). An instance setter with no declared return type inherits a return type from any superinterface member signature as normal, and defaults to void if there is no superinterface setter.
  • It's a compile-time error if a concrete setter declaration has a bottom type as return type, and control flow can exit the setter body (reach the end, reach any return statement, or even contain any return statement, but this is just the same rules as for any method body with a bottom return type).
  • operator []= counts as a setter.

Could work.
Not sure if it's worth the work, just to document that a setter throws.
For a static setter, it just means that you should never call it, so it might as well not be there.
This only makes sense for instance setters, but those also gave to be valid overrides, so any interface having a throwing setter implies that all subtypes must also throw.
That again only makes sense for a sub-interface that disables supertype APIs, which isn't great OO design (breaks subtype substitutability). Can be useful occasionally, but not great.

@mateusfccp
Copy link
Contributor Author

Yes, it would only be useful for instance setters, and only for a few cases. I feel like there are no downsides (except, obviously, for the resources spent to implement it), but maybe the benefits are not worthy enough.

I was thinking operator []= was already a setter (because it already doesn't allow anything but void). Does it have a different special case for the return type?

@sriramsowmithri9807

This comment was marked as off-topic.

@lrhn
Copy link
Member

lrhn commented Dec 11, 2024

I was thinking operator []= was already a setter

I think it is, just wanted to be certain that it was included since "setter" sometimes means "something declared using set" and sometimes means "something behaving as an assignment". Here we want the latter.

@rrousselGit
Copy link

One workaround is to mark such setter with a @Deprecated('do not use'), like:

@Deprecated('The list is immutable, do not use');
@override
void operator[]=(int index);

@mateusfccp
Copy link
Contributor Author

One workaround is to mark such setter with a @Deprecated('do not use'), like:

@Deprecated('The list is immutable, do not use');
@override
void operator[]=(int index);

This is an interesting workaround and may work, although the semantics conveyed are not correct.

@sriramsowmithri9807
Copy link

I agree that allowing setters to use Never as a return type would enhance clarity in cases like UnmodifiableListView.operator []=. It makes the intent explicit that such setters always throw and aligns well with null-safety principles. This seems like a reasonable and non-breaking improvement."

class UnmodifiableListView<T> {
  List<T> _items;

  UnmodifiableListView(this._items);

  Never operator []=(int index, T value) {
    throw UnsupportedError('Cannot modify an unmodifiable list.');
  }
}

void main() {
  var list = UnmodifiableListView<int>([1, 2, 3]);

  // Trying to assign a value will always throw an error
  list[0] = 10; // Throws UnsupportedError
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Proposed language feature that solves one or more problems
Projects
None yet
Development

No branches or pull requests

4 participants