1

I saw on the Dart documentation (Link) that yield* improves the performance of a recursive generator.

Iterable<int> naturalsDownFrom(int n) sync* {
    if (n > 0) {
        yield n;
        yield* naturalsDownFrom(n - 1);
    }
}

I don't get how that can be

Or how does the yield* works. What the differences between yield and yield* ?

  • Can you refer us to the documentation that claims it improves performance? In the example you give, it seems like if you substituted `yield*` for `yield`, it would simply fail type checking; `n` is an expression of type `int`, and `naturalsDownFrom(n-1)` is of type `Iterable`. Refer to [this post](https://stackoverflow.com/questions/57492517/difference-between-yield-and-yield-in-dart) for an explanation of the difference between the two forms. – Noah Dec 05 '21 at 01:47
  • Do you mean, using `yield*`, versus `yield`ing from a loop from `n` to `0` inside the generator? – Noah Dec 05 '21 at 02:12
  • @Noah I think that it wouldn't fail (Based on my exprience in other similar languages, so don't know if things are not the same in Dart) cause the supplied expression for `yield*` must be an Iterable of the same type of the generated value (Or a Stream if the function was defined using async). I add the link on the post. And the example is from the documentation also. – Imad Henaoui Dec 05 '21 at 03:02
  • Right, the docs are contrasting this example, where you `yield*` a new generator, with the one above it, where you `yield` the next value from inside a loop inside the same generator. In the `yield` example, when the generator resumes, it has to reload its state — its variables' values and so on — which is relatively costly. The `yield*` example doesn't have that problem. Yet they both generate the same sequence of values. This is an optimization similar to (or maybe the same as) [tail recursion](https://stackoverflow.com/questions/33923/what-is-tail-recursion). – Noah Dec 05 '21 at 04:02
  • Thnaks @Noah. Yes it's the same as tail recursion. – Imad Henaoui Dec 06 '21 at 18:02

1 Answers1

1

I wouldn't argue that you should use yield* for performance.

You should use yield* whenever you want to emit all the events of another stream inside an async* function.

The yield* differs from yield in that the latter emits a single value, and the former emits all the events of another stream.

Doing

yield* someStream;

is almost the same as doing:

await for (var value in someStream) {
  yield value;
}

That is, the yield* emits the same data events as the stream it works on.

The difference is that yield* also emits error events, and always emits the entire stream, where the await for stops at the first error event.

You should not make your function recursive unless necessary, just to use yield*, that's not going to help performance.

lrn
  • 64,680
  • 7
  • 105
  • 121