From 3267fc1ace530a37fecd05d0838809c6f0af22a7 Mon Sep 17 00:00:00 2001 From: David Thompson Date: Thu, 6 Feb 2014 21:18:32 -0500 Subject: Add new coroutine macro. * 2d/coroutine.scm (call-with-coroutine): New name for coroutine. (coroutine): New macro. (colambda, codefine, codefine*): Use call-with-coroutine. * examples/coroutine.scm: Use coroutine macro. * README.org: Update coroutine and agenda examples. --- 2d/coroutine.scm | 18 ++++++++++++------ README.org | 51 ++++++++++++++++++++++++++++++++------------------ examples/coroutine.scm | 20 +++++++++----------- 3 files changed, 54 insertions(+), 35 deletions(-) diff --git a/2d/coroutine.scm b/2d/coroutine.scm index 036cdc4..322f236 100644 --- a/2d/coroutine.scm +++ b/2d/coroutine.scm @@ -22,14 +22,15 @@ ;;; Code: (define-module (2d coroutine) - #:export (coroutine + #:export (call-with-coroutine + coroutine colambda codefine codefine*) #:replace (yield)) -(define (coroutine thunk) - "Calls a procedure that can yield a continuation." +(define (call-with-coroutine thunk) + "Apply THUNK with a coroutine prompt." (define (handler cont callback . args) (define (resume . args) ;; Call continuation that resumes the procedure. @@ -42,11 +43,16 @@ ;; Call procedure. (call-with-prompt 'coroutine-prompt thunk handler)) +;; emacs: (put 'coroutine 'scheme-indent-function 0) +(define-syntax-rule (coroutine body ...) + "Evaluate BODY as a coroutine." + (call-with-coroutine (lambda () body ...))) + ;; emacs: (put 'colambda 'scheme-indent-function 1) (define-syntax-rule (colambda args body ...) "Syntacic sugar for a lambda that is run as a coroutine." (lambda args - (coroutine + (call-with-coroutine (lambda () body ...)))) ;; emacs: (put 'codefine 'scheme-indent-function 1) @@ -57,7 +63,7 @@ coroutine." ;; Create an inner procedure with the same signature so that a ;; recursive procedure call does not create a new prompt. (define (name ...) . body) - (coroutine + (call-with-coroutine (lambda () (name ...))))) ;; emacs: (put 'codefine* 'scheme-indent-function 1) @@ -68,7 +74,7 @@ coroutine." ;; Create an inner procedure with the same signature so that a ;; recursive procedure call does not create a new prompt. (define* (name . formals) . body) - (coroutine + (call-with-coroutine (lambda () (apply name args))))) (define (yield callback) diff --git a/README.org b/README.org index 50a70b8..bee725e 100644 --- a/README.org +++ b/README.org @@ -168,33 +168,48 @@ exists a =wait= procedure to pause a coroutine and schedule it to be resumed later. - Using a coroutine and the agenda, the NPC script can be rewritten + Using a coroutine and an agenda, the NPC script can be rewritten such that it does not halt further program execution. #+BEGIN_SRC scheme (use-modules (2d agenda) - (2d coroutine)) + (2d coroutine) + (2d game)) - (schedule-next - (colambda () - (while #t - (walk 'up) - (wait 60) - (walk 'down) - (wait 60)))) + (coroutine + (while #t + (walk 'up) + (wait game-agenda 60) + (walk 'down) + (wait game-agenda 60))) #+END_SRC - =colambda= is a useful macro that is syntactic sugar for a lambda - expression executed as a coroutine. =schedule-next= accepts a - thunk (a procedure that takes 0 arguments) and schedules it to be - executed the next time the agenda is ticked. There are other ways - to schedule procedures such as =schedule=, =schedule-interval=, - and =schedule-every=. + =coroutine= is a useful macro that evaluates a block of code as a + coroutine. =wait= aborts the procedure and schedules the + continuation inside of an agenda. =game-agenda= is the main + agenda that is ticked at each iteration of the game update loop. + In this example, the script is paused for 1 second after each + step. Since guile-2d enforces a fixed timestep and updates 60 + times per second by default, 60 ticks is equivalent to 1 second. - Since guile-2d enforces a fixed timestep and updates 60 times per - second by default, waiting for 60 updates means that the NPC will - wait one second in between each step. + You can also use the agenda to schedule the evaluation of any + thunk even if it isn't a coroutine. + + #+BEGIN_SRC scheme + (define (hello) + (display "Hello, world! Sorry I'm late!\n")) + + (schedule game-agenda hello 600) + #+END_SRC + + =schedule= accepts a thunk (a procedure that takes no arguments) + and schedules it to be applied after a certain number of ticks, or + after 1 tick by default. In this example, the text "Hello, world! + Sorry I'm late!" is displayed after 10 seconds. There are other + ways to schedule procedures, too. =schedule-interval= applies a + thunk periodically, and =schedule-each= applies a thunk upon every + tick. ** REPL Driven Development diff --git a/examples/coroutine.scm b/examples/coroutine.scm index a479a66..45e8b39 100644 --- a/examples/coroutine.scm +++ b/examples/coroutine.scm @@ -15,17 +15,15 @@ ;; Simple script that moves the sprite to a random location every ;; second. -(codefine (script) - (set-sprite-position! - sprite - (vector2 (random window-width) - (random window-height))) - (wait game-agenda 15) - (set-sprite-rotation! sprite (random 360)) - (wait game-agenda 15) - (script)) - -(schedule game-agenda script) +(coroutine + (while #t + (set-sprite-position! + sprite + (vector2 (random window-width) + (random window-height))) + (wait game-agenda 15) + (set-sprite-rotation! sprite (random 360)) + (wait game-agenda 15))) (add-hook! draw-hook (lambda (dt alpha) (draw-sprite sprite))) -- cgit v1.2.3