From: pjb@informatimago.com (Pascal J. Bourguignon)
To: help-gnu-emacs@gnu.org
Subject: Re: 'Compiler' functionality for Emacs Lisp
Date: Thu, 29 Jul 2010 09:55:00 +0200 [thread overview]
Message-ID: <87bp9qiuy3.fsf@kuiper.lan.informatimago.com> (raw)
In-Reply-To: 87hbjjgdbd.fsf@decebal.nl
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/
next prev parent reply other threads:[~2010-07-29 7:55 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-07-29 3:46 'Compiler' functionality for Emacs Lisp Cecil Westerhof
2010-07-29 4:47 ` Fren Zeee
2010-07-29 7:55 ` Pascal J. Bourguignon [this message]
2010-07-29 9:54 ` Stefan Monnier
2010-07-29 11:47 ` Elena
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87bp9qiuy3.fsf@kuiper.lan.informatimago.com \
--to=pjb@informatimago.com \
--cc=help-gnu-emacs@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this external index
https://git.savannah.gnu.org/cgit/emacs.git
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.