*erik hilsdale*14 Nov 1999 02:58 UTC

Whenever I use let-values, I always write it as (let-values ((Formals Exp) ...) Exp ... Exp) rather than this SRFI's (let-values (Formals Exp) Exp ... Exp) I do understand that the extra parens are annoying when there's only one binding, which is why I frequently use one of two other forms when I only care to use one binding. When I'm thinking about monads I tend to use (bind (Formals Exp) Exp ... Exp) which works just as this SRFI's version. In most of my usual coding I use (with-values Exp Consumer) ; Consumer should evaluate to a procedure because (a) I'm pretty used to cps, (b) it's an extremely short macro, and (c) it only uses a trifle (two spaces) more horizontal space than bind. So I suppose you should count me in as another vote for making let-values look more like let, and perhaps supplying let*-values and letrec-values as well (though letrec-values is hard to write portably -- see below for my all-too-cryptic version). However, I'm _definitely_ against the special one-return-value case that Michael Sperber suggests, for all the reasons already stated. And while I agree that the one-decl let-values and the multi-decl let-values are syntactically distinguishable, I don't like that as an argument for choosing one over the other. Whatever this SRFI decides will have a certain weight. I view the fact that an implementation can provide an extended syntax as a bug, not a feature: I don't want to explain to students or co-workers that 'it works both ways' in implementation X. -erik (define-syntax bind (syntax-rules () ((bind (Formals Exp) Body0 Body ...) (call-with-values (lambda () Exp) (lambda Formals Body0 Body ...))))) (define-syntax with-values (syntax-rules () ((with-values Exp Consumer) (call-with-values (lambda () Exp) Consumer)))) (define-syntax let-values (syntax-rules () ((let-values () Body0 Body ...) (let () Body0 Body ...)) ((let-values ((Formals Exp) Decl ...) Body0 Body ...) (let ((temp (lambda () Exp))) (let-values (Decl ...) (call-with-values temp (lambda Formals Body0 Body ...))))))) (define-syntax let*-values (syntax-rules () ((let*-values () Body0 Body ...) (let () Body0 Body ...)) ((let*-values ((Formals Exp) Decl ...) Body0 Body ...) (call-with-values (lambda () Exp) (lambda Formals (let*-values (Decl ...) Body0 Body ...)))))) ;;; This one's pretty convoluted. Don't try this at home, kids. ;;; The general idea for this macro is to spend some cycles ;;; accumulating all of the variables in the formals, and associating ;;; with each one a temporary variable (generated magically through ;;; hygiene). This trick is also used in the letrec macro in R5RS. ;;; Here, as there, we use string cookies to simulate helper macros. ;;; letrec-values is more complicated because we need an inner and an ;;; outer accumulator. The outer accumulator iterates over the decls, ;;; each time gathering a new list of pairs of FormalVariable x Temp. ;;; That list is consed onto the list of all such pairs, and saved ;;; with the Formals and Exp of the decl. The inner accumulator ;;; iterates over the formal parameter lists/symbols/improper-lists ;;; themselves. (define-syntax letrec-values (syntax-rules () ((letrec-values ((Formals Exp) ...) Body0 Body ...) (letrec-values "OUTER" ((Formals Exp) ...) () () (begin Body0 Body ...))) ((letrec-values "OUTER" ((Formals Exp) Decl ...) TempsOut DeclsOut Body) ;; we need to process a new formals list. (letrec-values "INNER" Formals () Formals Exp (Decl ...) TempsOut DeclsOut Body)) ((letrec-values "OUTER" () ((Var Temp) ...) ((Formals Exp ((DeclVar DeclTemp) ...)) ...) Body) ;; we're done processing all the decls. Time to expand. (let ((Temp #f) ...) (call-with-values (lambda () Exp) (lambda Formals (set! DeclTemp DeclVar) ... #f)) ... (let ((Var Temp) ...) Body))) ((letrec-values "INNER" (Var . Rest) InnerTempsOut Formals Exp Decls TempsOut DeclsOut Body) ;; we found a new variable, so generate a temp and go on. (letrec-values "INNER" Rest ((Var temp) . InnerTempsOut) Formals Exp Decls ((Var temp) . TempsOut) DeclsOut Body)) ((letrec-values "INNER" () InnerTempsOut Formals Exp Decls TempsOut DeclsOut Body) ;; we're done with these formals. Go back to the outer loop. (letrec-values "OUTER" Decls TempsOut ((Formals Exp InnerTempsOut) . DeclsOut) Body)) ((letrec-values "INNER" Var InnerTempsOut Formals Exp Decls TempsOut DeclsOut Body) ;; we've found a rest variable. Go back to the outer loop. (letrec-values "OUTER" Decls ((Var temp) . TempsOut) ((Formals Exp ((Var temp) . InnerTempsOut)) . DeclsOut) Body))))