sigildocs

(sigil coroutines)

(sigil coroutines) - Bidirectional Coroutines

Coroutines that can both send values to and receive values from the caller. Unlike generators (which only yield), coroutines enable true bidirectional communication through send and receive.

Basic Usage

(import (sigil coroutines))

;; A coroutine that doubles input values
(define doubler
  (coroutine
    (let loop ((x (receive)))
      (loop (send (* x 2))))))

(coroutine-send doubler 5)   ; => 10
(coroutine-send doubler 21)  ; => 42

Control Flow Inversion

Coroutines are useful for inverting control flow. For example, in game loops where C code controls the main loop but Scheme code needs to appear as if it owns execution:

(define game-loop
  (coroutine
    (let loop ((state (receive)))
      (let ((new-state (update-game state)))
        (loop (send new-state))))))

Exports

coroutine?procedure

Check if obj is a coroutine.

(coroutine? (coroutine (send 1)))  ; => #t
(coroutine? '(1 2 3))              ; => #f

Check if a coroutine has finished execution.

A coroutine is done when its body returns (without calling send).

(define c (coroutine (send 1)))
(coroutine-done? c)    ; => #f
(coroutine-send c 'x)  ; => 1
(coroutine-done? c)    ; => #t
send-receiveprocedure

Send a value and receive the next input.

This is the low-level primitive used by the send macro. Prefer using the send macro for cleaner syntax.

Create a coroutine from a thunk.

The thunk should use send to yield values and receive to get values from the caller. Prefer using the coroutine macro for cleaner syntax.

(define c (make-coroutine
            (lambda ()
              (let loop ((x (receive)))
                (loop (send (* x 2)))))))
(coroutine-send c 5)   ; => 10
(coroutine-send c 10)  ; => 20
coroutinesyntax

Create a coroutine from body expressions.

The body can use send to yield values to the caller and receive to get values from the caller. This enables bidirectional communication where the coroutine and caller take turns exchanging data.

;; A coroutine that doubles input values
(define doubler
  (coroutine
    (let loop ((x (receive)))
      (loop (send (* x 2))))))

(coroutine-send doubler 5)   ; => 10
(coroutine-send doubler 21)  ; => 42

Send a value to a coroutine and get its response.

Resumes the coroutine, providing val as the result of its pending receive call. The coroutine runs until it calls send, and that sent value becomes the return value of coroutine-send.

Returns #f if the coroutine finishes without sending a value. Raises an error if the coroutine is already done.

(define echo (coroutine
               (let loop ((x (receive)))
                 (loop (send x)))))
(coroutine-send echo 'hello)  ; => hello
(coroutine-send echo 'world)  ; => world
sendsyntax

Send a value to the caller and receive the next input.

Suspends the coroutine, returning val to the caller's coroutine-send. The result of send is the value that the caller passes in the next coroutine-send call.

(define accumulator
  (coroutine
    (let loop ((total 0))
      (let ((n (receive)))
        (let ((new-total (+ total n)))
          (loop (send new-total)))))))
(coroutine-send accumulator 10)  ; => 10
(coroutine-send accumulator 5)   ; => 15
(coroutine-send accumulator 3)   ; => 18
receivesyntax

Receive a value from the caller.

Suspends the coroutine until the caller invokes coroutine-send. The value passed to coroutine-send becomes the result of receive. Typically used at the start of a coroutine to get the first input.

(define greeter
  (coroutine
    (let ((name (receive)))
      (send (string-append "Hello, " name "!")))))
(coroutine-send greeter "World")  ; => "Hello, World!"