Here's an improved patch that updates the manual and adds tests. Mark
>From 220fb56249b462ad1b205a45a4953e6dc857c96e Mon Sep 17 00:00:00 2001 From: Mark H Weaver <m...@netris.org> Date: Mon, 18 Mar 2013 20:01:12 -0400 Subject: [PATCH] SRFI-45: Support delayed expressions that return multiple values. * module/srfi/srfi-45.scm (eager): Accept any number of arguments. Store the list of arguments in the value record. Previously, only one argument was accepted, and that value was stored in the value record. (delay): Support expressions that return any number of arguments. (force): Return the list of values stored in the value record. * doc/ref/srfi-modules.texi (SRFI-45): Update docs. Remove typing for simplicity in discussing multiple values. * test-suite/tests/srfi-45.test: Add tests. --- doc/ref/srfi-modules.texi | 53 ++++++++++++++++++++++++----------------- module/srfi/srfi-45.scm | 16 +++++++------ test-suite/tests/srfi-45.test | 34 ++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 29 deletions(-) diff --git a/doc/ref/srfi-modules.texi b/doc/ref/srfi-modules.texi index af1afc0..099f27e 100644 --- a/doc/ref/srfi-modules.texi +++ b/doc/ref/srfi-modules.texi @@ -3833,45 +3833,54 @@ words, no program that uses the R5RS definitions of delay and force will break if those definition are replaced by the SRFI-45 definitions of delay and force. +Guile compatibly extends SRFI-45 to support multiple values. + @deffn {Scheme Syntax} delay expression -Takes an expression of arbitrary type @var{a} and returns a promise of -type @code{(Promise @var{a})} which at some point in the future may be -asked (by the @code{force} procedure) to evaluate the expression and -deliver the resulting value. +Takes an expression and returns a promise which at some point in the +future may be asked (by the @code{force} procedure) to evaluate the +expression and deliver the resulting value(s). @end deffn @deffn {Scheme Syntax} lazy expression -Takes an expression of type @code{(Promise @var{a})} and returns a -promise of type @code{(Promise @var{a})} which at some point in the -future may be asked (by the @code{force} procedure) to evaluate the -expression and deliver the resulting promise. +Takes an expression (which must evaluate to a promise) and returns a +promise which at some point in the future may be asked (by the +@code{force} procedure) to evaluate the expression and deliver the +resulting promise. @end deffn -@deffn {Scheme Procedure} force expression -Takes an argument of type @code{(Promise @var{a})} and returns a value -of type @var{a} as follows: If a value of type @var{a} has been computed -for the promise, this value is returned. Otherwise, the promise is -first evaluated, then overwritten by the obtained promise or value, and -then force is again applied (iteratively) to the promise. +@deffn {Scheme Procedure} force promise +Takes a promise and returns the associated value(s) as follows: If +value(s) have been computed for the promise, these value(s) are +returned. Otherwise, the promise is first evaluated, then overwritten +by the obtained promise or value(s), and then force is again applied +(iteratively) to the promise. @end deffn -@deffn {Scheme Procedure} eager expression -Takes an argument of type @var{a} and returns a value of type -@code{(Promise @var{a})}. As opposed to @code{delay}, the argument is -evaluated eagerly. Semantically, writing @code{(eager expression)} is -equivalent to writing +@deffn {Scheme Procedure} eager value ... +Takes any number of argument(s) and returns a promise. As opposed to +@code{delay}, the argument(s) are evaluated eagerly. Semantically, +writing @code{(eager expression)} is equivalent to writing @lisp (let ((value expression)) (delay value)). @end lisp However, the former is more efficient since it does not require -unnecessary creation and evaluation of thunks. We also have the -equivalence +unnecessary creation and evaluation of thunks. For expressions that +return a single value, we also have the equivalence @lisp (delay expression) = (lazy (eager expression)) @end lisp + +For expressions that return arbitrary numbers of values, we have the +equivalence + +@lisp +(delay expression) = (lazy (call-with-values + (lambda () expression) + eager)) +@end lisp @end deffn The following reduction rules may be helpful for reasoning about these @@ -3881,7 +3890,7 @@ usage semantics specified above: @lisp (force (delay expression)) -> expression (force (lazy expression)) -> (force expression) -(force (eager value)) -> value +(force (eager value ...)) -> value ... @end lisp @subsubheading Correct usage diff --git a/module/srfi/srfi-45.scm b/module/srfi/srfi-45.scm index 29b0393..a44cc97 100644 --- a/module/srfi/srfi-45.scm +++ b/module/srfi/srfi-45.scm @@ -1,6 +1,6 @@ ;;; srfi-45.scm -- Primitives for Expressing Iterative Lazy Algorithms -;; Copyright (C) 2010, 2011 Free Software Foundation, Inc. +;; Copyright (C) 2010, 2011, 2013 Free Software Foundation, Inc. ;; Copyright (C) 2003 André van Tonder. All Rights Reserved. ;; Permission is hereby granted, free of charge, to any person @@ -25,8 +25,8 @@ ;;; Commentary: -;; This is the code of the reference implementation of SRFI-45, slightly -;; modified to use SRFI-9. +;; This is the code of the reference implementation of SRFI-45, +;; modified to use SRFI-9 and to support multiple values. ;; This module is documented in the Guile Reference Manual. @@ -50,16 +50,18 @@ (define-syntax-rule (lazy exp) (make-promise (make-value 'lazy (lambda () exp)))) -(define (eager x) - (make-promise (make-value 'eager x))) +(define (eager . xs) + (make-promise (make-value 'eager xs))) (define-syntax-rule (delay exp) - (lazy (eager exp))) + (lazy (call-with-values + (lambda () exp) + eager))) (define (force promise) (let ((content (promise-val promise))) (case (value-tag content) - ((eager) (value-proc content)) + ((eager) (apply values (value-proc content))) ((lazy) (let* ((promise* ((value-proc content))) (content (promise-val promise))) ; * (if (not (eqv? (value-tag content) 'eager)) ; * diff --git a/test-suite/tests/srfi-45.test b/test-suite/tests/srfi-45.test index 573eea0..d72de60 100644 --- a/test-suite/tests/srfi-45.test +++ b/test-suite/tests/srfi-45.test @@ -258,3 +258,37 @@ ;; Commented out since it takes too long #; (test-equal 300000000 (force (times3 100000000))) ;==> bounded space + + +;====================================================================== +; Test memoization of multiple values (non-standard extension in Guile) + +(with-test-prefix "Multiple values (non-standard)" + + (let ((promise (delay (values 1 2 3)))) + (pass-if-equal "Multiple values delay" + '(1 2 3) + (call-with-values + (lambda () (force promise)) + list))) + + (let ((promise (eager 1 2 3))) + (pass-if-equal "Multiple values eager" + '(1 2 3) + (call-with-values + (lambda () (force promise)) + list))) + + (let ((promise (delay (values)))) + (pass-if-equal "Zero values delay" + '() + (call-with-values + (lambda () (force promise)) + list))) + + (let ((promise (eager))) + (pass-if-equal "Zero values eager" + '() + (call-with-values + (lambda () (force promise)) + list)))) -- 1.7.10.4