Dmitri Volkov
2024-09-29 20:56:46 UTC
Wrote a toy implementation of asynchronous programming using
continuations. Posting here in case anyone might find it interesting:
(define pop-effect!
(lambda (es)
(match (unbox es)
[`() `(exit (void))]
[`(,a . ,d)
(begin
(set-box! es d)
a)])))
(define queue-effect!
(lambda (e es)
(set-box!
es
(append (unbox es) (list e)))))
(define handle-effects
(lambda (es)
(let ([eh (let/cc k k)])
(match (pop-effect! es)
[`(exit ,any) any]
[`(wait-until ,time ,k)
(cond
[(> (current-milliseconds) time)
(begin
(k `(effect-info ,eh ,es))
(eh eh))]
[else
(begin
(queue-effect! `(wait-until ,time ,k) es)
(eh eh))])]
[`(output ,s ,k)
(begin
(println s)
(k `(effect-info ,eh ,es))
(eh eh))]
[`(continue ,k)
(begin
(k `(effect-info ,eh ,es))
(eh eh))]))))
(define exit
(lambda (any ei)
(match-let ([`(effect-info ,eh ,es) ei])
(begin
(queue-effect! `(exit ,any) es)
(eh eh)))))
(define wait
(lambda (ms ei)
(match-let ([`(effect-info ,eh ,es) ei])
(let/cc k
(begin
(queue-effect! `(wait-until ,(+ (current-milliseconds) ms)
,k) es)
(eh eh))))))
(define output
(lambda (s ei)
(match-let ([`(effect-info ,eh ,es) ei])
(let/cc k
(begin
(queue-effect! `(output ,s ,k) es)
(eh eh))))))
(define continue
(lambda (ei)
(match-let ([`(effect-info ,eh ,es) ei])
(let/cc k
(begin
(queue-effect! `(continue ,k) es)
(eh eh))))))
(define run
(lambda (l)
(let
([initial-effects
(map
(lambda (f)
`(continue ,f))
l)])
(handle-effects (box initial-effects)))))
; example of use
(run
(list
(lambda (ei)
(begin
(wait 5000 ei)
(output "a" ei)))
(lambda (ei)
(begin
(wait 3000 ei)
(output "c" ei)))
(lambda (ei)
(begin
(wait 500 ei)
(output "b" ei)))))
continuations. Posting here in case anyone might find it interesting:
(define pop-effect!
(lambda (es)
(match (unbox es)
[`() `(exit (void))]
[`(,a . ,d)
(begin
(set-box! es d)
a)])))
(define queue-effect!
(lambda (e es)
(set-box!
es
(append (unbox es) (list e)))))
(define handle-effects
(lambda (es)
(let ([eh (let/cc k k)])
(match (pop-effect! es)
[`(exit ,any) any]
[`(wait-until ,time ,k)
(cond
[(> (current-milliseconds) time)
(begin
(k `(effect-info ,eh ,es))
(eh eh))]
[else
(begin
(queue-effect! `(wait-until ,time ,k) es)
(eh eh))])]
[`(output ,s ,k)
(begin
(println s)
(k `(effect-info ,eh ,es))
(eh eh))]
[`(continue ,k)
(begin
(k `(effect-info ,eh ,es))
(eh eh))]))))
(define exit
(lambda (any ei)
(match-let ([`(effect-info ,eh ,es) ei])
(begin
(queue-effect! `(exit ,any) es)
(eh eh)))))
(define wait
(lambda (ms ei)
(match-let ([`(effect-info ,eh ,es) ei])
(let/cc k
(begin
(queue-effect! `(wait-until ,(+ (current-milliseconds) ms)
,k) es)
(eh eh))))))
(define output
(lambda (s ei)
(match-let ([`(effect-info ,eh ,es) ei])
(let/cc k
(begin
(queue-effect! `(output ,s ,k) es)
(eh eh))))))
(define continue
(lambda (ei)
(match-let ([`(effect-info ,eh ,es) ei])
(let/cc k
(begin
(queue-effect! `(continue ,k) es)
(eh eh))))))
(define run
(lambda (l)
(let
([initial-effects
(map
(lambda (f)
`(continue ,f))
l)])
(handle-effects (box initial-effects)))))
; example of use
(run
(list
(lambda (ei)
(begin
(wait 5000 ei)
(output "a" ei)))
(lambda (ei)
(begin
(wait 3000 ei)
(output "c" ei)))
(lambda (ei)
(begin
(wait 500 ei)
(output "b" ei)))))