dart-lang / language

Design of the Dart language
Other
2.66k stars 205 forks source link

Inline getters (and possibly setters) #2742

Open water-mizuu opened 1 year ago

water-mizuu commented 1 year ago

In some OOP based-langauges like Dart, C#, Javascript, we can define setters and getters that can used as "properties". This basically allows us to store some "properties" as just fast calculations of currently saved properties, instead of saving them directly into the class which can become problematic in more complex mutable classes.

class Person {
  String firstName;
  String lastName;

  String get fullName => "$firstName $lastName";
  set fullName(String name) {
    /// Logic that sets the [firstName] and [lastName] from [name]
  }
}

Now, I propose that the usage of gettters and setters be allowedoutside of classes, specifically, in standalone functions. Note that this should be essentially the same as using a closure, but with more syntax sugar that it is a variable rather than a function call.

void doSomething() {
  String name = "Bob";
  String title = "Builder";
  String get introduction => "$name the $title";
  String getIntroduction() => "$name the $title";

  print(introduction); /// Bob the Builder
  print(computedName()); /// Bob the Builder
  introduction = "Bob the Engineer" /// Compiler error, there is no setter for [introduction]
}

Now I'm thinking setter can also be allowed, but I'm not so sure of its semantics, so I don't know.

void doSomething() {
  String name = "Bob";
  String title = "Builder";
  String get introduction => "$name the $title";
  void set introduction(String full) {
    /// Logic that updates the [name] and [title] variables
  }
  String getIntroduction() => "$name the $title";

  print(introduction); /// Bob the Builder
  print(computedName()); /// Bob the Builder
  introduction = "Bob the Engineer" /// Allowed
  print(name) /// Bob
  print(title) /// Engineer
}

I understand that closures can do the trick, but I'm thinking of the syntax sugar rather the fact that in the same way that we can disguise these complex methods as "properties", we can also disguise these inline getters and setters as "variables." I look forward to hearing everyone's thoughts and ideas.

mateusfccp commented 1 year ago

It is possible to define getters/setters outside of classes, but only in the global scope.


int _a = 0;
int get a => _a;
set a(int value) => _a = value;

void main() {
  print(a);
  a = 10;
  print(a);
}

I don't really see much advantage to introduce function-scoped getters. Is it just to avoid the ()?

String get introduction => "$name the $title";
String introduction() => "$name the $title";

print(introduction); /// Bob the Builder
print(introduction()); /// Bob the Builder

However, introducing setters could be interesting, although I feel like it would be a feature that almost none would use and thus I wouldn't prioritize it over other requests.

water-mizuu commented 1 year ago

It is possible to define getters/setters outside of classes, but only in the global scope.

Ah yes, I was aware of this. I just did not mention it in the post.

I don't really see much advantage to introduce function-scoped getters. Is it just to avoid the ()?

With all honesty, I would say that yes, it is mainly for the syntax sugar of not having the function call. In the end, this is a suggestion for a sweeter quality of life change.

lrhn commented 1 year ago

You can use a late variable;

  String name = "Bob";
  String title = "Builder";
  late String introduction = "$name the $title";

A late variable is lazy, and will only be evaluated when you first read the variable, then the value will be cached. The initializing expression behaves much like a separate function/getter body (it cannot use await, even if the surrounding function is async), so it's very close to a local getter, just with automatic caching of the result.

The non-late version is computed eagerly. Which is definitely what you want if you know you'll use the value at least once, so the late one is only for the case where you might not use the value, and might also use it more than once (otherwise just write the expression at the one use-point).

I've considered local getters before, because sometimes it really feels like the thing you want (say int get fooCount => this.fooCount!; when you know the value will be non-null), but most of the time, simply introducing a new local variable will do fine. If I expect the value to change over time, looking like a getter can be misleading.

water-mizuu commented 1 year ago

You can use a late variable;

Ah of course, but late variables only execute once. What I am suggesting are "variables" that compute their value each time they are called. For example,

String name = "Bob";
String title = "Builder";
String get introduction => "$name the $title";
print(introduction); // Bob the Builder
title = "Manager";
print(introduction); // Bob the Manager

It's a far-fetched example, but since we have getters/setters in top-level and classes, I was thinking why not add it in this context as well? It's understandably a low-priority feature.

If I expect the value to change over time, looking like a getter can be misleading.

Now that I think about it, I can somehow agree. Knowing that this is possible using functions, in the end, again, it is just a syntactic sugar feature.