help-gnu-emacs
[Top][All Lists]
Advanced

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

Re: 'Compiler' functionality for Emacs Lisp


From: Pascal J. Bourguignon
Subject: Re: 'Compiler' functionality for Emacs Lisp
Date: Wed, 08 Dec 2010 15:21:40 -0000
User-agent: Gnus/5.101 (Gnus v5.10.10) Emacs/23.2 (gnu/linux)

Cecil Westerhof <Cecil@decebal.nl> writes:

> When using for example C++ the compiler does a lot of checks for you.
> For example it checks if all your variables are declared or used and
> it does even find potential memory leaks. Is there something like this
> for Emacs Lisp? I am writing bigger functions nowadays. Yesterday I
> found out that not all my variables where declared in a let block and
> I had forgotten to remove a few I where not using anymore. Is there
> something that could do these checks for me?

Lisp is fundamentally different from C++.

Perhaps you haven't noticed, but when you compile your lisp function,
you don't have to recompile the whole emacs!

This is something you would have to do with C++, to allow these global
checks.


About variables, again, emacs lisp variables are totally different
from C++ variables.  In C++, variables are lexical variables.  In
emacs lisp, all the variables are dynamic variables.  This means that
it is not an error to use a free variable in a function, or to define
(with let) a variable that doesn't seem to be used in the let body.
Because the variable may be defined in a different function that calls
yours, or used in a different function that you call.


(defun f ()
  (let ((x 1))
    ;; isn't x used?
    (g)))

(defun g ()
  (let ((y 2))
    ;; isn't y used?
    (list x ; x is used here!
          (h))))

(defun h ()
  y) ; y is used here!

(f)
;;  --> (1 2)


Of course, it's up to the caller to fulfil the contract of function
such as h, and to define their free variables:

(h)
;; --> Debugger entered--Lisp error: (void-variable y)

(let ((y 42)) (h))
;; --> 42


Now, even more:

(defun hh ()
  (list y x)) ; yet other free variables.

(defvar *hook* 'h)

(defun g ()
  (let ((y 2))
  (message "ghh %S" *hook*)
  (list x (funcall *hook*))))

(f)
;; --> (1 2)

(require 'cl)
(setf *hook* 'hh)
(f)
;; --> (1 (2 1))

which show that when this new g calls the function in *hook*, nobody
can know in advance what function will be called.  The emacs user may
at any time change the value of the variable *hook*, to call another
function.  Therefore there is absolutely no way to do a comprenhesive
and general compilation-time check of the use of emacs lisp variables.

You're in a dynamic world!  Anything can change at any time!




Now, if you want (as we all should in most cases, really) to use a
sublanguage where variables are used as if they were lexical
variables, you can easily enough write static analysis tools for emacs
lisp code.  For example, here is a function that extracts the free
variables from a lambda expression:

(require 'cl)
(defun* free-variables (expression &optional (bound-variables '()))
  (cond
    ((symbolp expression) (if (member expression bound-variables)
                             '()
                             (list expression)))
    ((atom expression)   '())
    ((eq 'lambda (car expression))
     (let ((bound-variables (append (cadr expression) bound-variables)))
       (mapcan (lambda (subexpr) (free-variables subexpr bound-variables))
               (cddr expression))))
    (t (mapcan (lambda (subexpr) (free-variables subexpr bound-variables))
               expression))))


(free-variables '((lambda (b) (+ ((lambda (x) (* 2 x)) a) b)) 3) '())
;; --> (+ * a)

(free-variables '(lambda () a) '())
;; --> (a)


Of course, you would have to process let, let*, macros, etc, etc, to
make it complete.

Other functions like this may be written to check that all variables
are used in their lexical scope, and you may also check that any
variable defined in a function f, that are visible in the scope of the
call to a function g, are not free variables in the function g (or any
function called from g).  If you ever see f calling a hook, for which
you cannot determine the set of functions that may be called at
run-time, then you would have to further check that the variables of f
are not free variables in all the functions in emacs lisp.
Well, apart from the cases where you do want to use a special variable.

For example, case-fold-search is a dynamic variable that is used by
some emacs lisp functions as a global parameter:

(defun f (cf)
  (let ((case-fold-search cf))
    (string-match "abc" "toto ABC abc toto")))

(list (f nil) (f t)) 
;; --> (9 5)


In Common Lisp, we would use a declaration to indicate that we want a
special variable (dynamic) instead of a normal variable (lexical):

#+common-lisp (defun f (cf)
                 (let ((case-fold-search cf))
                    (declare (special case-fold-search))
                    (do-something)))

Of course, in Common Lisp  since the default is lexical variables, the
compiler may check more easily when you're using a free variable, or
when you're not using a defined variable.  Perhaps you may want to use
emacs-cl, which is a Common Lisp implementation written in emacs-lisp,
and which would allow you to write your emacs functions in Common Lisp
instead of emacs lisp?


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/


reply via email to

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