[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