Here’s another clever trick. Don’t embed the data together with the logic, as I do with my directory aliases. Save it in a separate file and load it in using (read).
Okay, it’s not that clever – I do it all the time in Perl using YAML and what-not. Slightly concerning, though, is that I didn’t automatically translate the knowledge gained from Perl to lisp. What was I thinking?
The code fragment is beautiful:
(defvar variable-name) (with-temp-buffer (insert-file-contents-literally <filename>) (setq variable-name (read (current-buffer))))
There’s another way to do it without the need to create a temporary buffer by modifying the load-read-function variable which “specifies an alternate expression-reading function for load and eval-region to use instead of read. The function should accept one argument, just as read does.”
(defmacro setq-load (var file)
`(let ((load-read-function (lambda (x) (setq ,var (read x))))) (load ,file)))
And then call it like this:
(setq-load variable-name “/tmp/test.el”)
Hi Aaron,
That does look cool, but unfortunately it didn’t work for me:
(setq-load test-var “~/.emacs-data.el”)
Debugger entered–Lisp error: (void-function aa)
(aa bb cc dd …)
eval-buffer(# nil …)
It seems that what we expect for the data in the file is subtly different. In your code it treats the contents of a buffer as a string, whereas the macro i wrote treats it as a lisp expression that gets evaluated. So for my code to work you need to make sure that the contents of the file are quoted:
‘(aa bb cc dd)
instead of:
(aa bb cc dd)
If you use the latter form with my code it assume that it is a valid expression and tries to evaluate the function ‘aa’ with the rest of the list as arguments, hence the error “(void-function aa)”.
Which way is correct? I would say neither– it depends on whether or not you want the contents to be evaluated or not. The way you wrote yours is very similar to what is used to implement the directory-local variables in files.el:
(defun dir-locals-read-from-file (file)
“Load a variables FILE and register a new class and instance.
FILE is the name of the file holding the variables to apply.
The new class name is the same as the directory in which FILE
is found. Returns the new class name.”
(with-temp-buffer
(insert-file-contents file)
(let* ((dir-name (file-name-directory file))
(class-name (intern dir-name))
(variables (let ((read-circle nil))
(read (current-buffer)))))
(dir-locals-set-class-variables class-name variables)
(dir-locals-set-directory-class dir-name class-name
(nth 5 (file-attributes file)))
class-name)))
Hi Aaron,
Yes, that makes sense. Void-function errors are typical when a list that was supposed to be quoted is missing the quote.
Cheers
This is what I use:
(defun read-from-file (file)
(with-temp-buffer
(insert-file-contents-literally file)
(read (current-buffer))))
(setq variable (read-from-file “/tmp/foo.el”))
No quoting required.
Hi Nathan,
This is effectively the same as the fragment presented in the blog post above. Is it a standard emacs lisp idiom I wasn’t previously aware of then?