[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Another vote for more parens (re: let-values)



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))))