1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-05-20 19:50:24 +02:00

SRFI-45: Support multiple values; add promise? predicate.

* 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.
  (promise?): Export.

* 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.  Add FSF copyright for 2010
  and 2013.  Add missing year to André van Tonder's copyright notice.
This commit is contained in:
Mark H Weaver 2013-03-18 20:01:12 -04:00
parent 8150dfa1f2
commit 1d64b4edb9
3 changed files with 89 additions and 32 deletions

View file

@ -3833,45 +3833,58 @@ 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 break if those definition are replaced by the SRFI-45 definitions of
delay and force. delay and force.
Guile compatibly extends SRFI-45 to support multiple values. It also
adds @code{promise?} to the list of exports.
@deffn {Scheme Procedure} promise? obj
Return true if @var{obj} is an SRFI-45 promise, otherwise return false.
@end deffn
@deffn {Scheme Syntax} delay expression @deffn {Scheme Syntax} delay expression
Takes an expression of arbitrary type @var{a} and returns a promise of Takes an expression and returns a promise which at some point in the
type @code{(Promise @var{a})} which at some point in the future may be future may be asked (by the @code{force} procedure) to evaluate the
asked (by the @code{force} procedure) to evaluate the expression and expression and deliver the resulting value(s).
deliver the resulting value.
@end deffn @end deffn
@deffn {Scheme Syntax} lazy expression @deffn {Scheme Syntax} lazy expression
Takes an expression of type @code{(Promise @var{a})} and returns a Takes an expression (which must evaluate to a promise) and returns a
promise of type @code{(Promise @var{a})} which at some point in the promise which at some point in the future may be asked (by the
future may be asked (by the @code{force} procedure) to evaluate the @code{force} procedure) to evaluate the expression and deliver the
expression and deliver the resulting promise. resulting promise.
@end deffn @end deffn
@deffn {Scheme Procedure} force expression @deffn {Scheme Procedure} force promise
Takes an argument of type @code{(Promise @var{a})} and returns a value Takes a promise and returns the associated value(s) as follows: If
of type @var{a} as follows: If a value of type @var{a} has been computed value(s) have been computed for the promise, these value(s) are
for the promise, this value is returned. Otherwise, the promise is returned. Otherwise, the promise is first evaluated, then overwritten
first evaluated, then overwritten by the obtained promise or value, and by the obtained promise or value(s), and then force is again applied
then force is again applied (iteratively) to the promise. (iteratively) to the promise.
@end deffn @end deffn
@deffn {Scheme Procedure} eager expression @deffn {Scheme Procedure} eager obj ...
Takes an argument of type @var{a} and returns a value of type Takes any number of argument(s) and returns a promise. As opposed to
@code{(Promise @var{a})}. As opposed to @code{delay}, the argument is @code{delay}, the argument(s) are evaluated eagerly. Semantically,
evaluated eagerly. Semantically, writing @code{(eager expression)} is writing @code{(eager expression)} is equivalent to writing
equivalent to writing
@lisp @lisp
(let ((value expression)) (delay value)). (let ((value expression)) (delay value)).
@end lisp @end lisp
However, the former is more efficient since it does not require However, the former is more efficient since it does not require
unnecessary creation and evaluation of thunks. We also have the unnecessary creation and evaluation of thunks. For expressions that
equivalence return a single value, we also have the equivalence
@lisp @lisp
(delay expression) = (lazy (eager expression)) (delay expression) = (lazy (eager expression))
@end lisp @end lisp
More generally, the following equivalence holds:
@lisp
(delay expression) = (lazy (call-with-values
(lambda () expression)
eager))
@end lisp
@end deffn @end deffn
The following reduction rules may be helpful for reasoning about these The following reduction rules may be helpful for reasoning about these
@ -3881,7 +3894,7 @@ usage semantics specified above:
@lisp @lisp
(force (delay expression)) -> expression (force (delay expression)) -> expression
(force (lazy expression)) -> (force expression) (force (lazy expression)) -> (force expression)
(force (eager value)) -> value (force (eager obj ...)) -> (values obj ...)
@end lisp @end lisp
@subsubheading Correct usage @subsubheading Correct usage

View file

@ -1,6 +1,6 @@
;;; srfi-45.scm -- Primitives for Expressing Iterative Lazy Algorithms ;;; 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. ;; Copyright (C) 2003 André van Tonder. All Rights Reserved.
;; Permission is hereby granted, free of charge, to any person ;; Permission is hereby granted, free of charge, to any person
@ -25,8 +25,8 @@
;;; Commentary: ;;; Commentary:
;; This is the code of the reference implementation of SRFI-45, slightly ;; This is the code of the reference implementation of SRFI-45,
;; modified to use SRFI-9. ;; modified to use SRFI-9 and to support multiple values.
;; This module is documented in the Guile Reference Manual. ;; This module is documented in the Guile Reference Manual.
@ -36,8 +36,9 @@
#:export (delay #:export (delay
lazy lazy
force force
eager) eager
#:replace (delay force) promise?)
#:replace (delay force promise?)
#:use-module (srfi srfi-9)) #:use-module (srfi srfi-9))
(define-record-type promise (make-promise val) promise? (define-record-type promise (make-promise val) promise?
@ -50,16 +51,18 @@
(define-syntax-rule (lazy exp) (define-syntax-rule (lazy exp)
(make-promise (make-value 'lazy (lambda () exp)))) (make-promise (make-value 'lazy (lambda () exp))))
(define (eager x) (define (eager . xs)
(make-promise (make-value 'eager x))) (make-promise (make-value 'eager xs)))
(define-syntax-rule (delay exp) (define-syntax-rule (delay exp)
(lazy (eager exp))) (lazy (call-with-values
(lambda () exp)
eager)))
(define (force promise) (define (force promise)
(let ((content (promise-val promise))) (let ((content (promise-val promise)))
(case (value-tag content) (case (value-tag content)
((eager) (value-proc content)) ((eager) (apply values (value-proc content)))
((lazy) (let* ((promise* ((value-proc content))) ((lazy) (let* ((promise* ((value-proc content)))
(content (promise-val promise))) ; * (content (promise-val promise))) ; *
(if (not (eqv? (value-tag content) 'eager)) ; * (if (not (eqv? (value-tag content) 'eager)) ; *

View file

@ -1,6 +1,7 @@
;;; -*- mode: scheme; coding: utf-8; -*- ;;; -*- mode: scheme; coding: utf-8; -*-
;; Copyright André van Tonder. All Rights Reserved. ;; Copyright (C) 2010, 2013 Free Software Foundation, Inc.
;; Copyright (C) 2003 André van Tonder. All Rights Reserved.
;; ;;
;; Permission is hereby granted, free of charge, to any person ;; Permission is hereby granted, free of charge, to any person
;; obtaining a copy of this software and associated documentation ;; obtaining a copy of this software and associated documentation
@ -258,3 +259,43 @@
;; Commented out since it takes too long ;; Commented out since it takes too long
#; #;
(test-equal 300000000 (force (times3 100000000))) ;==> bounded space (test-equal 300000000 (force (times3 100000000))) ;==> bounded space
;======================================================================
; Test promise? predicate (non-standard Guile extension)
(pass-if "promise? predicate"
(promise? (delay 1)))
;======================================================================
; Test memoization of multiple values (non-standard Guile extension)
(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))))