[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Declaring a local dynamic variable?
From: |
Pascal J. Bourguignon |
Subject: |
Re: Declaring a local dynamic variable? |
Date: |
Sun, 22 Sep 2013 19:11:36 +0200 |
User-agent: |
Gnus/5.13 (Gnus v5.13) Emacs/24.2 (gnu/linux) |
Joost Kremers <joostkremers@yahoo.com> writes:
> Hi,
>
> If I purposefully use a local dynamic variable as in:
>
> (defun foo ()
> (let* ((my-counter 0)
> (var (or (bar 'one)
> (bar 'two)
> (bar 'three))))
> (do-something-with var)
> (issue-a-message-depending-on-the-value-of my-counter)))
>
> (defun bar (arg)
> (setq my-counter (1+ my-counter))
> (do-something-with arg)
> (and-return-result))
>
> the byte compiler will issue a warning about referring to a free
> variable in `bar'. What is the proper way of letting the byte compiler
> know that it shouldn't worry about this particular variable, without
> silencing it completely? I don't want to disable all warnings, or even
> just the warnings about free variables.
>
> Right now, I'm using a `(defvar my-counter)' inside the function
> definition of `bar', but that looks a bit strange. Is there a better or
> "more proper" way of doing this?
CL has the (declare (special var…)) declaration for that.
(defun f ()
(declare (special var))
(print var))
(defun g ()
(let ((var 42))
(declare (special var))
(f)))
(g) ; prints 42.
(defun h ()
(let ((var 33)) ; lexical variable
(g)))
(h) ; prints 42
To implement that in emacs lisp, you could define defun, let, let* and
locally in a separate package, oops, no package in emacs.
To implement that in emacs lisp, you could define defun@, let@,
let*@ and locally@ as macros that will check for the special
declarations and define the variables as symbol-macrolets expanding to
(symbol-value 'variable), and that save the old symbol-value and restore
it. Something like:
(defun split-declarations (body)
(loop
for b on body
while (and (listp (car b)) (eq 'declare (caar b)))
append (cdar b) into declarations
finally (return (list declarations b))))
(assert (equal (split-declarations
'((declare (special a b))
(declare (integer a) (character b))
(declare (special c))
(print a)))
'(((special a b) (integer a) (character b) (special c))
((print a)))))
(defun split-bindings (bindings)
(list (mapcar (lambda (b)
(cond
((symbolp b) b)
((or (atom b) (cddr b))
(error "Invalid binding: %S -- a binding should be a symbol or
a list of two elements, a symbol and an expression." b))
(t (first b))))
bindings)
(mapcar (lambda (b)
(if (atom b)
nil
(second b)))
bindings)))
(assert (equal (split-bindings '(a (b 42) (c d)))
'((a b c) (nil 42 d))))
(defun split-special-variables (declarations)
(loop
for decl in declarations
if (eq 'special (car decl))
append (cdr decl) into specials
else
collect decl into other-decls
finally (return (list specials other-decls))))
(assert (equal
(split-special-variables
(first (split-declarations '((declare (special a b))
(declare (integer a) (character b))
(declare (special c))
(print a)))))
'((a b c) ((integer a) (character b)))))
(defvar *unbound* (list 'unbound))
(defun special-bind@ (var val expression)
(let ((save (gensym)))
`(let ((,save (if (boundp ',var)
(symbol-value ',var)
*unbound*)))
(unwind-protect
(symbol-macrolet ((,var (symbol-value ',var)))
(setf (symbol-value ',var) ,val)
,expression)
(if (eq ,save *unbound*)
(makunbound ',var)
(setf (symbol-value ',var) ,save))))))
(defun lexical-bind@ (var val expression)
`(let ((,var ,val) ,expression)))
(defun bind@ (specials vars vals expression)
(if (null vars)
expression
(bind@ specials (cdr vars) (cdr vals)
(if (member (car vars) specials)
(special-bind@ (car vars) (car vals) expression)
(lexical-bind@ (car vars) (car vals) expression)))))
(defmacro let@ (bindings &rest body)
(destructuring-bind (vars vals) (split-bindings bindings)
(destructuring-bind (declarations body) (split-declarations body)
(destructuring-bind (special-variables declarations)
(split-special-variables declarations)
(let ((temps (mapcar (lambda (var) (declare (ignore var)) (gensym))
vars)))
`(let ,(mapcar* (function list) temps vals)
,(bind@ special-variables (nreverse vars) (nreverse temps)
`(progn ,@body))))))))
(defun specially@ (vars expression)
`(symbol-macrolet ,(mapcar (lambda (var)
`(,var (symbol-value ',var)))
vars)
,expression))
(defmacro locally@ (&rest body)
(destructuring-bind (declarations body) (split-declarations body)
(message "d=%S b=%S" declarations body)
(destructuring-bind (special-variables declarations)
(split-special-variables declarations)
(message "s=%S d=%S" special-variables declarations)
(specially@ special-variables
(if declarations
`(locally (declare ,@declarations) ,@body)
`(progn ,@body))))))
(defun f ()
(locally@
(declare (special x))
(print x)))
(defun g ()
(let@ ((x 42))
(declare (special x))
(f)))
(g)
prints: 42
--> 42
(defun h ()
(let@ ((var 33)) ; lexical variable
(g)))
(h)
prints:42
--> nil ; where does this come from? Is there a bug in emacs-version "24.2.1"?
For defun@, one would have to take care of special declarations for
parameters, and splitting docstrings from declarations and body.
--
__Pascal Bourguignon__
http://www.informatimago.com/
- Re: Declaring a local dynamic variable?, (continued)
- Re: Declaring a local dynamic variable?, Joost Kremers, 2013/09/23
- Message not available
- Re: Declaring a local dynamic variable?, Joost Kremers, 2013/09/23
- Re: Declaring a local dynamic variable?, Barry Margolin, 2013/09/23
- Re: Declaring a local dynamic variable?, Joost Kremers, 2013/09/23
- Re: Declaring a local dynamic variable?, Barry Margolin, 2013/09/23
- Re: Declaring a local dynamic variable?, Joost Kremers, 2013/09/25
Re: Declaring a local dynamic variable?,
Pascal J. Bourguignon <=
Message not available
- Re: Declaring a local dynamic variable?, Joost Kremers, 2013/09/23
- Re: Declaring a local dynamic variable?, Joost Kremers, 2013/09/25
- Re: Declaring a local dynamic variable?, Stefan Monnier, 2013/09/25
- Message not available
- Re: Declaring a local dynamic variable?, Barry Margolin, 2013/09/25
- Re: Declaring a local dynamic variable?, Stefan Monnier, 2013/09/25