# Draft coroutine factory return type
Pros/cons of varieties of `next()`.
```typescript
/**
* A multi-step computation that may be
* scheduled for a step by calling the
* [Generator.next] method.
*
* Block lambdas that extend [GeneratorFn] specify
* factories for [Generator]s.
*
* Terminology note:
* Coroutines may be represented as Generators
* where [Generator.next] starts or resumes
* computation and the coroutine pauses itself
* when done with the step.
* We avoid the term "coroutine" because many
* programmers associate it narrowly with
* parallelism.
*/
interface Generator<PAUSE_T, BUBBLE extends Bubble> {
/**
* Starts/resumes the computation and runs until
* it pauses or completes.
*
* If the computation was [done] prior to the
* start of the call or it is done at the end,
* returns [Null].
* This is the case for a [GeneratorFn] that
* [returns][snippet/builtin/return].
*
* If the computation pauses, returns the value
* associated with the decision to pause.
* For a [GeneratorFn], this is the value passed to
* [snippet/builtin/pause].
*/
public next(): PAUSE_T | Null | BUBBLE;
/**
* True if subsequent calls to [next] would do
* no work and return [Null].
*/
public done: Boolean;
/**
* Should be called once it is realized that
* [next] is not going to be called again to
* release resources needed by [next].
*
* After `close()` exits, [done] will be true.
*/
public close(): Void;
}
```
## Variant 1: Null means no result / not-resumable.
```typescript
public next(): PAUSE_T | Null | BUBBLE;
```
Pros:
- Simple interface. No object allocation.
Cons:
- Either *PAUSE_T* needs to exclude null or there's an ambiguity about what `.next() == null` means. If the latter, code that uses it generically needs to check `.done` separately and know to do so.
- *null* means "no result," not "don't expect any result after this"
## Variant 2: Option type. None means no result / not-resumable.
```typescript
public next(): Optional<PAUSE_T> | BUBBLE;
```
Pros:
- Unambiguous
- Reuses std type that is a semantic match
- Idiom `when (g.next()) { ... }`
Cons:
- If a target language has a result type especially for generators, like JavaScript's `{ next, done }` record, the backend needs to adapt.
- May require more pause value boxing when PAUSE_T is a pass-by-copy/primitive by copy.
## Variant 3: Generator helper type.
```typescript
public next(): GeneratorResult<PAUSE_T> | BUBBLE;
```
```typescript
sealed interface GeneratorResult<out PAUSE_T> {
public done: Boolean;
}
class GeneratorDone extends GeneratorResult<Never> {
public get done(): Boolean { true }
}
class NextResult<PAUSE_T> extends GeneratorResult<PAUSE_T> {
public value: PAUSE_T;
public get done(): Boolean { false }
}
```
Pros:
- Unambiguous
- Idiom `when (g.next()) { ... }`
- Compiles to nice code on most backends.
Cons:
- Duplicative of optionals in Temper code.
Meh:
- If a target language has an optional type but does not have native support for generators, we can still connect this type to optionals.
## Variant 4: Bubble if next() ends without pausing.
```typescript
public next(): PAUSE_T | Bubble;
```
Pros:
- No need for BUBBLE type parameter
- Slightly easier translation of *Generator.next* on target languages whose native *Generator* type throws (see near bottom about Python's *StopIteration*) to indicate no-next value.
Cons:
- Mild ambiguity between next() failed and next() successfully concluded correctly that there are no more values.
## Example usage
We need to check `done` in some fashion before using a value returned from next. Here's an example:
```typescript
let myGenerator = makeAGenerator { () extends GeneratorFn;;
pause(1);
// Client needs to call `next()` before continuing.
console.log("About to return");
};
let x = myGenerator.next();
if (!myGenerator.done) { // not done
console.log(x); // -> 1
}
let y = myGenerator.next();
if (!myGenerator.done) { // done
console.log(y);
}
```
That might enable loops of this form for null-returning:
```typescript
// Variant 1, null-returning
while (true) {
let x = myGenerator.next() orelse do {
console.log("I'm so sad.");
myGenerator.close();
bubble();
};
if (myGenerator.done) {
break; // Guaranteed null `x` but meh.
}
process(x);
}
myGenerator.close();
```
Or for helper return type:
```typescript
// Variant 3, using match
while (true) {
let next = myGenerator.next() orelse do {
console.log("I'm so sad.");
myGenerator.close();
bubble();
};
match (next) {
is NextResult -> process(next.value);
else -> break;
}
}
myGenerator.close();
```
Or for bubbling:
```typescript
// Variant 4, bubbling
while (true) {
let x = myGenerator.next() orelse do {
if (myGenerator.done) {
// Don't know if I should be sad?
break;
}
console.log("I'm so sad.");
myGenerator.close();
bubble();
};
process(x);
}
myGenerator.close();
```
## Connecting to backend types: exceptions when no-next.
Some target languages raise an exception from a generator when
there is no next value.
For example, Python does `raise StopIteration()`.
```python
>>> def f():
... yield 1
... yield 2
... yield 3
...
>>> i = f()
>>> type(i)
<class 'generator'>
>>> i.__next__()
1
>>> i.__next__()
2
>>> i.__next__()
3
>>> i.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
```
Java does have `NoSuchElementException` but that is usually only
thrown when a client fails to check `.hasNext()` properly.
How do we translate `myGenerator.next()` uses from Temper code using
a generator that might have come from backend code?
### Variant 1, 2, 3: trap exception
```python=
# Python support code
def temper_generator_next(generator):
try:
return generator.__next__()
except StopIteration:
# For variant 1
return None # Temper null
# Or for variant 2
return EmptyOption
# Or for variant 3
return DoneResult
# Then we can translate `foo.next()` to
temper_generator_next(foo)
```
### Variant 4
Make sure we treat *StopIteration* as a bubble.