1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-05-20 11:40:18 +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
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
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 obj ...
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
More generally, the following equivalence holds:
@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 +3894,7 @@ usage semantics specified above:
@lisp
(force (delay expression)) -> expression
(force (lazy expression)) -> (force expression)
(force (eager value)) -> value
(force (eager obj ...)) -> (values obj ...)
@end lisp
@subsubheading Correct usage

View file

@ -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.
@ -36,8 +36,9 @@
#:export (delay
lazy
force
eager)
#:replace (delay force)
eager
promise?)
#:replace (delay force promise?)
#:use-module (srfi srfi-9))
(define-record-type promise (make-promise val) promise?
@ -50,16 +51,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)) ; *

View file

@ -1,6 +1,7 @@
;;; -*- 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
;; obtaining a copy of this software and associated documentation
@ -258,3 +259,43 @@
;; Commented out since it takes too long
#;
(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))))