[Top][All Lists]

[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 <address@hidden> 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))

(g) ; prints 42.

(defun h ()
  (let ((var 33)) ; lexical variable

(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) 
     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)
               ((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))))
        (mapcar (lambda (b)
                  (if (atom b)
                      (second b)))

(assert (equal (split-bindings '(a (b 42) (c d)))
               '((a b c) (nil 42 d))))

(defun split-special-variables (declarations)
     for decl in declarations
     if (eq 'special (car decl))
     append (cdr decl) into specials
     collect decl into other-decls
     finally (return (list specials other-decls))))

(assert (equal
          (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)
            (symbol-macrolet ((,var (symbol-value ',var)))
              (setf (symbol-value ',var) ,val)
         (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)
      (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)) 

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

(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 ()
   (declare (special x))
   (print x)))

(defun g ()
  (let@ ((x 42))
     (declare (special x))

prints: 42
--> 42

(defun h ()
  (let@ ((var 33)) ; lexical variable

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

reply via email to

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