Tips on Emacs Lisp programming
I think EmacsLisp is getting to be a great application base, a really good language and environment to write programs in, not just a fancy editor. A number of people seem to agree and are trying it out.
Here's some tips and tricks distilled from my 15 years of using EmacsLisp to help budding Lisp hackers in Emacs.
Do use a modern Emacs
The latest version of Emacs is 24. It's not added to a whole lot of operating system repositories yet but for Debian and Ubuntu you can get it easily by using this repository or PPA at http://emacs.naquadah.org/.
Using the latest version will give you access to lexical scope, built in packaging and lots more goodness.
I am working on a yum equivalent of that APT repository. Donating might make that quicker.
Cut. And also paste
You don't have to have a reusable module for everything, it's ok to cut and paste code sometimes.
There is a massive load of EmacsLisp on github which you can look at and copy.
There's a wealth of code out there, use it
There are Emacs librarys for OAuth (and Oauth2, in ELPA) and for talking to Org Mode and for writing IRC bots or interfacing to PostgreSQL or MySQL or MongoDB. Talking to HTTP and SMTP and handling and parsing XML is built into Emacs. Building parsers and things that understand data definition languages in all sorts of formats is also well covered in EmacsLisp.
Basically, pretty much everything you could want to do as a programmer is there. Sometimes it's not packaged very well, but that's coming.
Use name based namespaces
EmacsLisp does not have a module system. Many people call this a serious weakness but in my experience it's easy to work around it.
There is a heirarchy you should follow:
- a package is a file or list of files that may be installed as a package
- hopefully defined as a package using ELPA standards
- this is important so you can write code that is portable and has declared dependancies
- a module is a file in a package that is
providedso it can be
- there may be one or many modules in a package
- functions, classes, variables, constants, structs and other top level definitions all exist in a module.
Given this heirarchy we can adopt some simple but consistent rules:
- Name classes with a suffix
- Name important constants in UPPERCASE.
- Use the package name in your module names.
- If you have more then one module in your package, then have one top-level module that is the main module of your package, if your package is called
somepackagethen have a
- Use the module name in the names of your functions, variables, methods, classes.
(defun packagename-module-function (args) ...)
- or in your top-level module:
(defun packagename-function (args) ...)
--to indicate private variables or functions, like:
(defun packagename--function (args) ...)
- macros often need different names, try and use your package and module still, eg this is ok:
(defmacro with-package-module-thing (...) )
Understand packages and autoloads
Autoloads allow lazy loading of EmacsLisp library code. This is basically how they work:
- functions or variables marked as
;;;### autoloadare compiled into functions that first load the rest of the library and then call the original definition of the function
- when Emacs starts all the autoloads from all packages are loaded; this is fast because autoloads are very small
- when an autoloaded function is called its autoload causes the library to load
Do use CL
Emacs core has always suggested not requiring
cl or using:
(eval-when-compile (require 'cl))
Do write tests
ERT is included in Emacs 24 and provides a good way of writing tests.
You can even use my fakir library to mock tricky bits of Emacs like processes or files.
It's best to write one module of tests for each module of code. A project with a lot of tests is Elnode. Each test module is named after the module that it tests which seems like more good advice.
Do write function docs
You can use my wikidoc program to make HTML documentation from function and variable doc.
Write command scripts with EmacsLisp
Shell scripts are always quick and easy but sometimes you want a program to do a little more, shell becomes unwieldy fast and you want to switch to a proper programming language. EmacsLisp can do this pretty well, here's an example I've used in my blog before:
:;exec emacs -batch -Q -l "$0" -f main "$@" (require 'cl) (toggle-debug-on-error) (defun main () (interactive) (destructuring-bind (package &optional elpa-parent) command-line-args-left ;; Make the elpa dir for this if we need to. (when (and elpa-parent (not (file-exists-p elpa-parent))) (make-directory elpa-parent t)) ;; Package stuff (setq package-user-dir (concat (or (concat elpa-parent "/") user-emacs-directory) ".elpa")) (setq package-archives '(("gnu" . "http://elpa.gnu.org/packages/") ("marmalade" . "http://marmalade-repo.org/packages/"))) (package-initialize) (package-refresh-contents) (if (and (file-exists-p (expand-file-name package)) (not (file-directory-p (expand-file-name package)))) (package-install-file (expand-file-name package)) ;; Else must just be a package (package-install (intern package))))) ;; End
The first line is what does the magic of making Emacs run the script in batch mode (no windows). Note that what you want to do with the script in that bang line is pretty flexible:
:;exec emacs -batch -Q -l "$0" -f main "$@"
main is the function to call and the
$@ is of course shell for the arguments.
Emacs is very powerful at file manipulation, it's buffer mechanism is very useful for certain programming tasks that may be very difficult in shell.
More on scripts in the EmacsWiki.
It's not weird to use Object Orientation in EmacsLisp
Object Oriented coding is suffering a bit of a backlash in some circles but it's still very useful in lots of cicrumstances, where you are trying to declare a category of things that are quite complex for example.
CommonLisp has CLOS, a very comprehensive Object Orientation programming API and EmacsLisp has EIEIO, an implementation of CLOS.
Here's a trivial example of EmacsLisp classes and methods:
(defclass some-userc nil ((id :initarg :id :initform (lambda () (some-user--make-uuid)) :documentation "The id of the person") (name :type string :initarg :name :initform "" :documentation "Name of the person")) "A user record.") (defun some-user--make-uuid () ; functions can be used by constructors "1213243") (defmethod some-user-greeting ((user some-userc) ; this is a type specifier &optional daytime) "Methods are functions and have docstrings." (if daytime (message "good morning %s" (oref user name)))) (let ((user (some-userc "nic" :name "nic ferrier"))) ; make a user (some-user-greeting user t)) ; call the method
Macro writing needs settings
eval-expression-print-level needs to be set to
off) so that you can always see what's happening.
Don't write too many macros
Macro use cases are:
- hiding access to resources
- building DSLs
- and abstracting control flow
Of these only the first is common in most programming, the second is obvious when you need it and the last you should think quite carefully before doing, it's normally for Lisp language hackers, not hackers in Lisp language.
PS That doesn't mean you should never write control flow macros, just think hard before doing it.
Closures are cool but harder to debug
If you need anything more than a tiny callback function then make a tiny callback that calls an external function with the right data:
(let ((x somecapturedthing)) (lambda (httpcon) (real--implementation httpcon x)))
The full function is nearly always private but it can be more easily instrumented for debugging so you can easily see what's going on in that code. It's also useful sometimes to separate stuff out like this temporarily. It's pretty easy to do quickly, it's probably easy enough that you could write some EmacsLisp to do it automatically.
Use the manuals
The Elisp Cookbook on the EmacsWiki is also a great resource.
Do ask questions
So that's it
These are the things I can think of that people don't know. If you're thinking of looking at a Lisp for your next programming project then I really recommend EmacsLisp, it's a very practical language with lots to recommend it.
I got the value of
eval-expression-print-level wrong, it should
nil to be turned off,
0 will stop any structure
printing. This is now fixed.