I\'m trying to port yield and yield from from Python to Scheme.
Here is an implementation I\'ve done:
(define (coroutine ro
Kudos to @Sylwester for a great answer.
The difficult part is making yield available to the generator function. datum->syntax creates a syntax object, and requires you to provide another syntax object from which the context for the new object is taken. In this case, we can use stx which has the same context as the function passed into the macro.
If people find it helpful, I use a simpler version:
(define-syntax (set-continuation! stx)
"Simplifies the common continuation idiom
(call/cc (λ (k) (set! name k) ))"
(syntax-case stx ()
[(_ name . body)
#`(call/cc (λ (k)
(set! name k)
. body))]))
(define-syntax (make-generator stx)
"Creates a Python-like generator.
Functions passed in can use the `yield` keyword to return values
while temporarily suspending operation and returning to where they left off
the next time they are called."
(syntax-case stx ()
[(_ fn)
#`(let ((resume #f)
(break #f))
(define #,(datum->syntax stx 'yield)
(λ (v)
(set-continuation! resume
(break v))))
(λ ()
(if resume
(resume #f)
(set-continuation! break
(fn)
'done))))]))
An example of its usage:
(define countdown
(make-generator
(λ ()
(for ([n (range 5 0 -1)])
(yield n)))))
(countdown)
=> 5
...
(countdown)
=> 1
(countdown)
=> 'done
(countdown)
=> 'done