dart-lang / dart-lang/language
Custom generators (like `async`, `async*` and `sync*`)
- Dominant language
- TeX
- Stars
- 2.9k
- Forks
- 239
- Avg merge
- 2d 18h
- Merged PRs (30d)
- 14
Description
I think it could be really interesting if we could create our own generators like `async`, `async*`, and `sync*`
I noticed that really, a lot of the features present in each kind already exists in normal code, ala `Completer`, `StreamController`, and custom iterables
I looked at `yield` and `yield*` and equated it to `StreamController.add` and `StreamController.addAll`
I recognized that `async*` returned an `Iterable>`, which `Stream` implements, not dissimilar to `sync*`
I looked at `await`, and recognized how it flattened the callback hell of `.then`s by just giving us the value and early returning the `Completer.future`
I looked at these and realized they all return before a single line of code ever runs.
And I looked at #2567 https://github.com/flutter/flutter/issues/25280 and https://github.com/flutter/flutter/issues/51752 and remembered how @rrousselGit mentioned that "hooks" would be a language feature akin to doing `final x = use thing;`
So the feature that comes to mind is, what if we could make our own functional generators? extend the dart language "directly"?
What if we could do something like:
```dart
// Effect, Hook, whatever
// myGenerator could register disposals and such into the resulting object, to be handled elsewhere
Effect<...> makeThing() myGenerator {
// primitives would directly implement Effect (similar to flutter_hook's Hook class)
final (get, set) = use state(0);
// additional code
return (get(), set);
}
// elsewhere:
// something like
final (value, setValue) = handler.use(makeThing()) // receives Effect
// where:
T handler.use(Effect effect) {
registerDispose(effect.dispose);
// more logic
return readEffect(effect);
}
```
and of course, that logic can be anything.
a naive example:
```dart
// pseudocode, may not be the best api
generator stringbuilder returns String {
final buffer = StringBuffer();
operator yield(String str) => buffer.write(str);
operator yield*(Iterable strs) => buffer.writeAll(str);
String operator return() => buffer.toString();
}
Iterable makeManyStrings(int count, String str) sync* {
for (final i = 0, i < count; i++) {
yield str;
}
}
String makeString() stringbuilder {
yield 'Hello ';
yield 'World!';
yield '\n';
yield* makeManyStrings(3, 'Hehe');
}
final String result = makeString(); // Hello World!\nHeheHeheHehe
```
simple, but it shows how simple it _could_ be, yet gives a ton of flexibility in terms of power and complexity
isn't declarative code better?
- well okay, not always, but this can make code that would be better declarative, to _be_ declarative
I think this can be a super powerful feature, allowing custom(ish) syntax to exist in packages (or sdks, hint hint) which could possibly simplify a lot.
It would make algebraic effects unnecessary since you'd have to provide them in the returned object before you can use any data.
the presence of `yield` would also provide "free" lazy loading for certain cases, like it does with stream and iterable
existing generators like `async`, `async*` and `sync*` could even be implemented in this feature, which would have the benefits of being able to look at its implementation (for better understanding and discovery) and also make it possible to add documentation to it.
Sometimes I try to hover over `await` or `yield(*)` to see what they would say and... nothing.
we can even reuse some existing syntax - `await`, `yield` etc would be definable `operator`s
There's a lot of potential, and this would be a pretty major feature, so I'd like to hear some thoughts!
Contributor guide
Research direction
Start with the issue's examples of custom generators, yield/yield*, await, and existing async*/sync* behavior, then review the linked issues #2567, #25280, and #51752. The issue names no files or tests and does not define a concrete implementation target; a useful outcome would require a narrowed language-design proposal and agreed validation plan.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- dart
- Domain
- compilers
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 22/100