Another vote for more parens (re: let-values)
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))))