# Title

Coroutine Generators

# Author

Marc Nieper-Wißkirchen

# Status

This SRFI is currently in final status. Here is an explanation of each status that a SRFI can hold. To provide input on this SRFI, please send email to `srfi-190@nospamsrfi.schemers.org`. To subscribe to the list, follow these instructions. You can access previous messages via the mailing list archive.

• Draft #1 published: 2020-03-30
• Finalized: 2020-06-11

# Abstract

This SRFI defines syntax to create SRFI 121/158 coroutine generators conveniently and in the flavor of Python generator functions.

# Rationale

In some sense, the most fundamental generator is the coroutine generator. SRFI 121/158 allow the easy creation of a coroutine generator by calling the procedure `make-coroutine-generator`.

For example, the following evaluates to a generator that produces the series 0, 1, and 2:

```(make-coroutine-generator
(lambda (yield)
(do ((i 0 (+ i 1)))
((<= 3 i))
(yield i)))))
```

These generators are similar to the generator functions of Python. When using `make-coroutine-generator`, though, the programmer has to wrap the actual coroutine body in a lambda and has to explicitly bind the name of the yielding procedure.

In this SRFI, a syntax that evaluates to coroutine generators is defined that simplifies the creation of coroutine generators and avoids the need of binding the yielding procedure to a name.

Using the syntax of this SRFI, the above example becomes:

```(coroutine-generator
(do ((i 0 (+ i 1)))
((<= 3 i))
(yield i)))
```

The yielding procedure can be passed to helper procedures.

```(let ((yield-square (lambda (yield i) (yield (* i i)))))
(coroutine-generator
(do ((i 0 (+ i 1)))
((<= 3 i))
(yield-square yield i))))
```

An equivalent version using a local macro would be:

```(let-syntax ((yield-square (syntax-rules () ((_ i) (yield (* i i))))))
(coroutine-generator
(do ((i 0 (+ i 1)))
((<= 3 i))
(yield-square i))))
```

A Python generator function is not completely equivalent to a coroutine generator in the sense of SRFI 121/158 and this SRFI as a Python generator function has to be called first to return a generator. This SRFI includes convenience syntax to define a generator function in the sense of Python.

The following defines a procedure that returns a generator producing the series 0, 1, …, n − 1.

```(define-coroutine-generator (g n)
(do ((i 0 (+ i 1)))
((<= n i))
(yield i)))
```

All syntax defined in this SRFI is hygienic. In particular, the identifier `yield` is exported (and bound to syntax) by any library implementing this SRFI.

While the sample implementation uses syntax parameters and identifier macros, a specific implementation that is being shipped by a particular Scheme system doesn't have to. In fact, this is a reasonable thing for those systems for which `make-coroutine-generator` from SRFI 121/158 is slow because `call/cc` is slow in these systems (unfortunately, such systems exist). As `coroutine-generator` SRFI 190 is just syntax and offers no more than Python's generator functions (note that Python doesn't have `call/cc`), those systems can translate it directly into fast code that does not have to rely on first-class continuations. For this translation to be easy (and no more difficult than that what CPython has to do), it is crucial that `yield` is lexically scoped (and thus confined to the expanded body of the coroutine) and not dynamically bound.

# Specification

## Syntax

`(coroutine-generator 〈body〉)`

Creates a generator from a coroutine. When evaluated, immediately returns a generator g. When g is called, the definitions and expressions in `〈body〉` are evaluated until the yielding procedure of the coroutine generator is called. Calling the yielding procedure of the coroutine generator causes the evaluation of `〈body〉` to be suspended, and g returns the value passed to yield.

Whether this generator is finite or infinite depends on the behavior of `〈body〉`. If the last expression in `〈body〉` returns, it is the end of the sequence — g returns an end-of-file object from then on.

`yield`

Evaluates to the yielding procedure in the (expansion of the) `〈body〉` of a coroutine generator.

It is an error to evaluate `yield` outside the body of a coroutine generator.

`(define-coroutine-generator 〈name〉 〈body〉)`

`(define-coroutine-generator (〈name〉 . 〈formals〉) 〈body〉)`

Expands into

`  (define 〈name〉 (coroutine-generator 〈body〉))`
and
`  (define (〈name〉 . 〈formals〉) (coroutine-generator 〈body〉))`

respectively.

# Implementation

The following implementation based on SRFI 121/158 makes use of syntax parameters as defined in SRFI 139 and identifier syntax as defined in the R6RS.

```(define-syntax-parameter yield
(lambda (stx)
(syntax-case stx ()
(_ (error "yield used outside coroutine generator" stx)))))

(define-syntax coroutine-generator
(lambda (stx)
(syntax-case stx ()
((_ . body)
#'(make-coroutine-generator
(lambda (%yield)
(syntax-parameterize ((yield (identifier-syntax %yield)))
. body)))))))

(define-syntax define-coroutine-generator
(lambda (stx)
(syntax-case stx ()
((_ name . body) #'(define name (coroutine-generator . body))))))
```

Any implementation will define the identifiers `yield`, `define-coroutine-generator`, and `coroutine-generator` in the library ```(srfi 190)```.

# Acknowledgements

Credits go to the authors of SRFI 121 and SRFI 158, Shiro Kawai, John Cowan, and Thomas Gilray.