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

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

This page is part of the web mail archives of SRFI 11 from before July 7th, 2015. The new archives for SRFI 11 contain all messages, not just those from before July 7th, 2015.



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