emacs-devel
[Top][All Lists]
Advanced

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

Re: Sweeter Emacs Lisp


From: Pascal J. Bourguignon
Subject: Re: Sweeter Emacs Lisp
Date: Sat, 10 Aug 2013 12:08:05 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.2 (gnu/linux)

Thien-Thi Nguyen <address@hidden> writes:

> () Stefan Monnier <address@hidden>
> () Mon, 22 Jul 2013 17:04:04 -0400
>
>    > RMS suggested instead:
>    >  (cond VAR (CONDITION [BODY...])
>    >            ...)
>
>    As I pointed out back then, a more general solution is a way to
>    let-bind new variables in between cond clauses, as in
>
>       (cond
>        (<test1> <body1>)
>        (let x <foo>)
>        (<test2> <body2>))
>
>    which would be used in cases where we currently use
>
>       (let (x)
>         (cond
>          (<test1> <body1>)
>          ((progn (setq x <foo>) <test2>) <body2>))

I don't like it.  The general idiom in lisp, and including in emacs
lisp, is to have a close correspondance between parentheses and lexical
scope. 

Whether x is in the englobing scope, or in a scope covering only the
remaining clauses, in both cases it's bad because it's not reflected by
the sexp structure of the form.

In this aspect, RMS' suggestion is better.

I would advise a form rather like:

                       (letcond 
                          ((f) 1)
                          (let* ((x (g))
                                (y (h x)))
                            ((= x y) 2)
                            ((< x y) 3)
                            (let ((z (p)))
                              ((< x z) 4))
                            (t 5))
                          ((q) 6)
                          (t   0))




(defun letcond-generate-let-clause (let-clause)
  (destructuring-bind (let bindings &body body) let-clause
    `((,let ,bindings (cond ,@(letcond-generate-cond-clauses body))))))

(defun letcond-generate-cond-clauses (clauses)
  (mapcar (lambda (clause)
            (if (member (first clause) '(let let*))
                (letcond-generate-let-clause clause)
                clause))
          clauses))

(defmacro letcond (&rest clauses)
  `(cond ,@(letcond-generate-cond-clauses clauses)))

(pprint (macroexpand-1 '(letcond 
                          ((f) 1)
                          (let* ((x (g))
                                (y (h x)))
                            ((= x y) 2)
                            ((< x y) 3)
                            (let ((z (p)))
                              ((< x z) 4))
                            (t 5))
                          ((q) 6)
                          (t   0))))

--> (cond
      ((f) 1)
      ((let*
           ((x (g)) (y (h x)))
         (cond
           ((= x y) 2)
           ((< x y) 3)
           ((let
                ((z (p)))
              (cond ((< x z) 4))))
           (t 5))))
      ((q) 6)
      (t 0))



Notice how emacs already knows how to indent it to show the correct
scopes!

Yes, within a letcond, let and let* take another meaning (they are
interpreted specially by the letcond macro) when in the place of a
clause condition.  In general I dislike those overloading, but I think
in this case it may be acceptable and the best solution, even if it
prevents you to use a variable named let or let*;  you can always use
cond to test such a variable, or use (identity let):

(letcond
 (let ((let 'let))
   ((identity let) let)
   (t              nil)))
--> let

vs. the surprizing:

(letcond
 (let ((let 'let))
   (let nil)
   (t   let)))
--> let

(letcond
 (let ((let 'let))
   (let let)
   (t   nil)))
Debugger entered--Lisp error: (wrong-type-argument sequencep let)



-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
A bad day in () is better than a good day in {}.  
You know you've been lisping too long when you see a recent picture of George 
Lucas and think "Wait, I thought John McCarthy was dead!" -- Dalek_Baldwin




reply via email to

[Prev in Thread] Current Thread [Next in Thread]