8000 A variable declaration used only for interface purposes has unwanted semantics · Issue #44 · dart-lang/language · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

A variable declaration used only for interface purposes has unwanted semantics #44

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

Closed
eernstg opened this issue Oct 11, 2018 · 10 comments
Closed
Labels
request Requests to resolve a particular developer problem

Comments

@eernstg
Copy link
Member
eernstg commented Oct 11, 2018

[Handled according to #44 in Dart today.]

Consider the following declaration, where we wish to specify a class that has a property x which is intended to be implemented differently in different subtypes:

abstract class A {
  int get x;
  void set x(int value);
}

This design makes it possible for subclasses (and other subtypes) to override both the getter and the setter with a simple int x;, in the case where the desired implementation is just a simple storage location:

class B1 extends A {
  int x;
}

But why wouldn't we simply declare the property as int x; in the abstract superclass A and rely on the implicitly induced getter and setter with exactly the same signatures?

Assume that the appropriate relationship is extends because we wish to inherit the implementation of some other methods.

We could then not use a plain int x; in A, because that would incur a subtle memory leak in subclasses where the desired implementation is more complex:

class B2 extends A {
  get x => ...;
  set x(value) => ...;
}

Instances of B2 would now (where we consider an A that uses int x;) have storage for a variable corresponding to A.x, and that storage would we wasted unless some (surely unnatural and confusing) occurrences of super.x were added here and there to make use of it.

So there may be good reasons to declare an abstract getter and setter in order to allow subclasses to make their own choices about how to implement the given property.

The problem is that this is more verbose, and it requires certain parts of the declaration to be redundant and consistent: The name needs to be the same in the getter and the setter, and the return type of the getter needs to be at least related to the argument type of the setter (and they should be the same type almost without exception).

@eernstg eernstg added the request Requests to resolve a particular developer problem label Oct 11, 2018
@Hixie
Copy link
Hixie commented Oct 11, 2018

Is this a common problem? Most of the time I see interfaces only declare a getter. When a getter and a setter are both provided and abstract, it's usually the case in my experience that the setter is going to need to do work (e.g. mark the object as dirty), such that you wouldn't actually override them with a field anyway.

@leafpetersen
Copy link
Member

Is this a common problem?

It certainly occurs. Strong mode originally disallowed virtual field overrides (this situation), and there was code rejected buy that. Probably wouldn't be too hard to get some data on the frequency.

@a14n
Copy link
a14n commented Oct 12, 2018

A little related there's a similar issue for external that can't be used on fields. With js-interop it could be worth to allow external on fields. It'd avoid declaring both getter and setter.

@eernstg
Copy link
Member Author
eernstg commented Oct 12, 2018

@Hixie wrote:

you wouldn't actually override them with a field anyway

Of course, the counterpart #45 is just a tiny convenience feature that we've had on the table for a while, but the benefit that it does provide is that it allows abstract classes that need to declare a getter/setter pair to be more concise, and that would still be true even if there won't ever be a subclass that overrides this pair with a field.

@greglittlefield-wf
Copy link
greglittlefield-wf commented Dec 12, 2019

When trying out the NNBD DartPad preview today, I realized that it's not possible to declare non-nullable fields with the field-based syntax as-is. Using the example in the issue description, trying to declare A.x with a field as-is results in an error:

abstract class A {
  int x; // error: Non-nullable instance field 'x' must be initialized
}

Using late seems like it'd be a workaround:

abstract class {
  late int x;
}

It would be really nice if it could just be declared abstract instead 😄.

I've wanted something like #45 for a while now to improve our code generation for https://github.com/Workiva/over_react, which also experiences this issue.

We generate implementations of user-authored classes that override fields, similar to the example in the description:

mixin FooProps on UiProps {
  String foo;
}

// .g.dart
mixin $FooPropsImpl on FooProps {
  @override 
  String get foo => props['foo'];

  @override 
  set foo(String value) => props['foo'] = value;
}

@Hixie
Copy link
Hixie commented Dec 12, 2019

You can do:

  int get x;
  set x(int value);

...to declare it abstract.

@greglittlefield-wf
Copy link

Yeah, the main issue with that, as the end paragraph of the issue description notes, is that it's a bit verbose/redundant.

In the OverReact use case, there are many of these properties, and they can change a lot during the course of development. So, having a syntax that's as terse as possible is important to maintain a good user experience when authoring the properties.

That's ultimately why we settled on fields over abstract getters/setters, even though it creates an unused field.

@lrhn
Copy link
Member
lrhn commented Jan 27, 2020

A proposal for this feature, allowing external too: https://github.com/dart-lang/language/blob/master/working/00044%20-%20Abstract-External%20Variables/proposal.md

@rrousselGit
Copy link
rrousselGit commented Jan 27, 2020

This is an edge-case, but:
As a workaround to the lack of data-classes, I was thinking of using noSuchMethod to implement a typed Map<Symbol, dynamic> (with an automatic toString/==/debugFillProperties/toJson/clone)

It works fine (besides the inability to have final properties), but the syntax is kinda bad due to this:

class Example extends Record {
  Example() {
    a = 42;
  }

  int get a;
  set a(int a);
}

// OR

class Example extends Record implements _Example {
  Example() {
    a = 42;
  }
}

class _Example {
  int a;
}

Having to declare both a getter and a setter makes it not very readable.

It's obviously an edge-case/temporary workaround, but being able to declare an abstract field would make this use-case a lot better

@eernstg
Copy link
Member Author
eernstg commented Feb 26, 2021

Handled according to #44 in Dart today.

@eernstg eernstg closed this as completed Feb 26, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
request Requests to resolve a particular developer problem
Projects
None yet
Development

No branches or pull requests

7 participants
0