Thursday, January 24, 2008

Emergency Elisp

Are you an Emacs user but don't know Lisp? Welcome to my first Emacs Lisp primer! This should hopefully help get you over the hurdle so you can have more control over your Emacs sessions.

There are lots of ways to do things in Lisp, and some are "Lispier" than others. I'm going to focus on how to do things you probably already know how to do from C++ or Java.

I'm mostly focusing on the language itself, since that's arguably the hardest part. There are tons of Emacs-specific APIs that you can learn how to use from the documentation.

Lisp is good at some things (like code that generates code) and not so good at others (like arithmetic expressions). I will generally avoid talking about good vs. bad, and just talk about how to do things. Emacs Lisp is like any other language – you get used to it eventually.

Most Lisp introductions try to give you the "Tao of Lisp", complete with incense-burning, chanting, yoga and all that stuff. What I really wanted in the beginning was a simple cookbook for doing my "normal" stuff in Lisp. So that's what this is. It's an introduction to how to write C, Java or JavaScript code in Emacs Lisp, more or less.

Here goes. Let's see how short I can make it. I'll start with the boring (but hopefully familiar) lexical tokens and operators, then move on to how to implement various favorite statements, declarations and other programming constructs.

Quick Start

Lisp is written as nested parenthesized expressions like (+ 2 3). These expressions are sometimes called forms (in the sense of "shapes".)

There are also "atoms" (leaf nodes, basically) that are not parenthesized: strings, numbers, symbols (which must be quoted with apostrophe for use as symbols, like 'foo), vectors, and other miscellany.

There are only single-line comments: semicolon to end of line.

To set a variable named foo to the value "bar":
(setq foo "bar")  ; setq means "set quoted"

To call a function named foo-bar with arguments "flim" and "flam":
(foo-bar "flim" "flam")

To compute the arithmetic expression (0x15 * (8.2 + (7 << 3))) % 2:
(% (* #x15 (+ 8.2 (lsh 7 3))) 2)

In other words, arithmetic uses prefix notation, just like lisp function calls.

There's no static type system; you use runtime predicates to figure out the type of a data item. In elisp, predicate functions often end with "p". I'll let you figure out what it stands for.

Important: You can (and should) experiment with Lisp in the *scratch* buffer. You can evaluate an expression and see its result in any of several ways, including:

  1. Put your cursor after the last close-paren and type C-j (control + j)

  2. Put your cursor inside the expression and type M-C-x (alt + control + x)

  3. Put your cursor after the last close-paren and type C-x C-e


The first approach spits the result into the *scratch* buffer, and the next two echo it into the minibuffer. They all also work for atoms – expressions not in parens such as numbers, strings, characters and symbols.

Lexical Stuff


Lisp has only a handful of lexical tokens (i.e. atomic program elements).

Comments:

Single-line only. They start with a semicolon:
(blah blah blah)   ;  I am a comment

Strings:

Double-quoted only.
"He's said: \"Emacs Rules\" one time too many."

You can embed newlines in strings, like so:
"Oh Argentina!
Your little tin of pink meat
Soars o'er the Pampas"

Characters:

  • ?x is the syntax for an ASCII character: ? followed by the character.

  • e.g.: ?a is ascii 97 ('a'), ? (that is, question-mark space) is ascii 32 (' ').

  • Some need to be escaped, such as ?\(, ?\) and ?\\

  • Emacs 22+ has unicode support. Out of scope for this primer.

Characters are just int values internally, so you can use arithmetic operations on them (for instance, to iterate through ?a to ?z).

Numbers:

  • Integers are 29 bits of precision (not the usual 32). -32, 0, 157, etc.

  • Binary: start with #b, e.g. #b10010110

  • Octal: #o[0-7]+, e.g. #o377

  • Hexadecimal: start with #x, e.g. #xabcd, #xDEADBEE

  • Floating-point: the usual. -10.005, 0.0, 3.14159265 (64 bits of precision.)

  • Scientific: the usual. 6.02e23, 5e-10


The variables most-positive-fixnum and most-negative-fixnum are the largest and smallest integers representable in Emacs Lisp without bignum support. Emacs 22+ comes with a fancy bignum/math library called calc, if you need it. Arithmetic operations overflow and underflow the way you'd expect (in, say, C or Java.)

Booleans

The symbol t (just a letter 't' by itself) is true.

The symbol nil is false (and also means null).

In Emacs Lisp, nil is the only false value; everything else evalutes to true in a boolean context, including empty strings, zero, the symbol 'false, and empty vectors. An empty list, '(), is the same thing as nil.

Arrays

Elisp has fixed-sized arrays called "vectors". You can use square-brackets to create a pre-initialized literal vector, for instance:
[-2 0 2 4 6 8 10]
["No" "Sir" "I" "am" "a" "real" "horse"]
["hi" 22 120 89.6 2748 [3 "a"]]

Note that you do not (and cannot) use commas to separate the elements; use whitespace.

Vectors can have mixed-type elements, and can be nested. You usually use the function make-vector to create them, since literal vectors are singletons, which can be surprising.

Lists

Lisp makes heavy use of linked lists, so there's lexical syntax for them. Anything in parentheses is a list, but unless you quote it, it will be evaluated as a function call. There are various ways to quote things in Lisp:
(quote (1 2 3)) ; produces the list (1 2 3) with no list-element evaluation
'(1 2 3)  ; apostrophe is shorthand for (quote (...))
; note that it goes _outside_ the left-paren
(list 1 (+ 1 1) 3) ; also produces (1 2 3), since it evaluates the elements first
`(1 ,(+ 1 1) 3)  ; another (1 2 3) via a template system called "backquote"
There's a lot more that could be said about lists, but other people have already said it.

Pairs

You can set the head and tail (also known as car and cdr) fields of a lisp link-list node struct (also known as a cons cell) directly, using it as a 2-element untyped struct. The syntax is (head-value . tail-value), and you have to quote it (see above).

A common lookup-table data-structure for very small data sets is an associative list (known as an alist). It's just a list of dotted pairs, like so:
'( (apple . "red")
(banana . "yellow")
(orange . "orange") )
Emacs Lisp has built-in hashtables, bit-vectors, and miscellaneous other data structures, but there's no syntax for them; you create them with function calls.

Operators


Some operations that are typically operators in other languages are function calls in elisp.

Equality

Numeric equality: (= 2 (+ 1 1)) Single-equal. Yields t or nil. Works for floats too.

Not-numerically-equal: (/= 2 3) I know, it looks like assign-divide-equal. But it's not.

Value equality: (eq 'foo 2) Like Java ==. Works for ints, symbols, interned strings, and object references. Use eql for floating-point numbers (or just =).

Deep (structural) equality: use equal, as in:
(equal '(1 2 (3 4)) (list 1 2 (list 3 (* 2 2))))  ; true

The equal function is like Java's Object.equals(). Works for lists, vectors, strings, and just about anything else.

String

Strings don't have any operators, but there are lots of string functions. Some common ones:
(concat "foo" "bar" "baz")  ; yields "foobarbaz"

(string= "foo" "baz") ; yields nil (false). Can also use equal.

(substring "foobar" 0 3) ; yields "foo"

(upcase "foobar") ; yields "FOOBAR"

Do M-x apropos RET \bstring\b RET to see a list of functions related to strings.

Arithmetic

Easiest to show as a table...

C/Java/JS Operator Emacs Lisp Example Result
+ + (+ 1 2 3 4 5) 15
- - (- 6 2 3) 1
* * (* 2 -1 4.2) -8.4
/ / (/ 10 3) 3 (use floats for float div)
% % (% 10 2) 0
<< lsh (lsh 1 5) 32
>> ash (negative amount) (ash -32 -4) -2
>>> lsh (negative amount) (lsh 32 -4) 2
++ incf (requires 'cl library) (incf x 6) x+6
-- decf (ditto) (decf x 5) x-5
? : (ternary) (if test-expr then-expr else-expr) (if t 3 4) 3
&& and (and t t t nil) nil
|| or (or nil nil nil t) t
! (logical-not) not (not 3) nil
~ (bit-not) lognot (lognot #b1001) -10
^ (bit-xor) logxor (logxor 5 3) 6
& (bit-and) logand (logand 1 3) 1
| (bit-or) logior (logior 1 3) 3
< < (< 5 3) nil
> > (> 5 3) t
<= <= (<= 3 3) t
>= >= (>= 5 3) t
. (field access) see setf below n/a n/a
[] (array access) aref/aset (aref [2 4 6] 1) 4

Statements


This section has some recipes for simple Java-like statements. It's not comprehensive – just some recipes to get you going.

if/else

Case 1: no else clause: (if test-expr expr)

Example:
(if (>= 3 2)
(message "hello there"))

Case 2: else clause: (if test-expr then-expr else-expr)
(if (today-is-friday)         ; test-expr
(message "yay, friday") ; then-expr
(message "boo, other day")) ; else-expr

If you need multiple expressions (statements) in the then-expr, you wrap them with a call to progn, which is like curly-braces in C or Java:
(if (zerop 0)
(progn
(do-something)
(do-something-else)
(etc-etc-etc)))

You don't need the progn around the else-expr – everything after the then-expr is considered to be part of the else-expr. Hence:
(if (today-is-friday)
(message "yay, friday")
(message "not friday!")
(non-friday-stuff)
(more-non-friday-stuff))

Case 3: else-if clause: Just nest 'em. Or use cond (see below).
(if 'sunday
(message "sunday!") ; then-expr
(if 'saturday ; else-if
(message "saturday!") ; next then-expr
(message ("weekday!")))) ; final else

Case 4: no else-if, multiple body expressions – use when:

If you don't have an else-clause, then you can use the when macro, which provides an implicit progn:
(when (> 5 1)
(blah)
(blah-blah)
(blah blah blah))

You can also use unless, which is like when but inverts the sense of the test:
(unless (weekend-p)
(message "another day at work")
(get-back-to-work))

switch

Elisp has two versions of the classic switch statement: cond and case.

Elisp does not have a table-lookup optimization for switch, so cond and case are just syntax for nested if-then-else clauses. However, if you have more than one level of nesting, it looks a lot nicer than if expressions. The syntax is:
(cond
(test-1
do-stuff-1)
(test-2
do-stuff-2)
...
(t
do-default-stuff))

The do-stuff parts can be any number of statements, and don't need to be wrapped with a progn block.

Unlike classic switch, cond can handle any test expression (it just checks them in order), not just numbers. The downside is that it doesn't have any special-casing for numbers, so you have to compare them to something. Here's one that does string compares:
(cond
((equal value "foo") ; case #1 – notice it's a function call to `equal' so it's in parens
(message "got foo") ; action 1
(+ 2 2)) ; return value for case 1
((equal value "bar") ; case #2 – also a function call (to `+')
nil) ; return value for case 2
(t ; default case – not a function call, just literal true
'hello)) ; return symbol 'hello

The final t default clause is optional. The first matching clause is executed, and the result of the entire cond expression is the result of the last expression in the matching clause.

The 'cl (Common Lisp) package bundled with Emacs provides case, which works if you're comparing numbers or symbols, so in a sense it works more like standard switch. Example:
(case 12
(5 "five")
(1 "one")
(12 "twelve")
(otherwise
"I only know five, one and twelve.")) ; result: "twelve"

With case you can use either t or otherwise for the default case, but it must come last.

It's cleaner to use case when you can get away with it, but cond is more general.

while

Elisp has a relatively normal while function: (while test body-forms)

Example, which you can evaluate in your *scratch* buffer:
(setq x 10
total 0)
(while (plusp x) ; while x is positive
(incf total x) ; add x to total
(decf x)) ; subtract 1 from x

First we set two global variables, x=10 and total=0, then run the loop. Then we can evaluate the expression total to see that its value is 55 (the sum of the numbers 1 to 10).

break/continue

Lisp has a facility for upward control-flow transfers called catch/throw. It's very similar to Java or C++ exception handling, albeit possibly somewhat lighter-weight.

To do a break from inside a loop in elisp, you put a (catch 'break ...) outside the loop, and a (throw 'break value) wherever you want to break inside the loop, like so:

Emacs Lisp Java

(setq x 0 total 0)
(catch 'break
(while t
(incf total x)
(if (> (incf x) 10)
(throw 'break total))))
var x = total = 0;
while (true) {
total += x;
if (x++ > 10) {
break;
}
}

The symbol 'break is arbitrary, but is probably a nice choice for your readers. If you have nested loops, you might consider 'break-outer and 'break-inner in your catch expressions.

You can (throw 'break nil) if you don't care about the "return value" for the while-loop.

To continue a loop, put a catch expression just inside the loop, at the top. For instance, to sum the numbers from 1 to 99 that are not evenly divisible by 5 (artificially lame example demonstrating use of continue):

Emacs Lisp Java
(setq x 0 total 0)
(while (< x 100)
(catch 'continue
(incf x)
(if (zerop (% x 5))
(throw 'continue nil))
(incf total x)))
var x = total = 0;
while (x < 100) {
x++;
if (x % 5 == 0) {
continue;
}
total += x;
}

We can combine these examples to show using a break and continue in the same loop:

Emacs Lisp JavaScript
(setq x 0 total 0)
(catch 'break
(while t
(catch 'continue
(incf x)
(if (>= x 100)
(throw 'break nil))
(if (zerop (% x 5))
(throw 'continue nil))
(incf total x))))
var x = total = 0;
while (true) {
x++;
if (x >= 100) {
break;
}
if (x % 5 == 0) {
continue;
}
total += x;
}

All the loops above compute the value 4000 in the variable total. There are better ways to compute this result, but I needed something simple to illustrate break and continue.

The catch/throw mechanism can be used across function boundaries, just like exceptions. It's not intended for true exceptions or error conditions – Emacs has another mechanism for that, discussed in the try/catch section below. You should get comfortable using catch/throw for normal jumps and control transfer in your Elisp code.

do/while

Pretty much all iteration in Emacs Lisp is easiest using the loop macro from the Common Lisp package. Just do this to enable loop:
(require 'cl)  ; get lots of Common Lisp goodies

The loop macro is a powerful minilanguage with lots of features, and it's worth reading up on. I'll use it in this primer to show you how to do basic looping constructs from other languages.

You can do a do/while like so:
(loop do
(setq x (1+ x))
while
(< x 10))

You can have any number of lisp expressions between the do and while keywords.

for

The C-style for-loop has four components: variable initialization, the loop body, the test, and the increment. You can do all that and more with the loop macro. For instance, this arbitrary JavaScript:
// JavaScript
var result = [];
for (var i = 10, j = 0; j <= 10; i--, j += 2) {
result.push(i+j);
}

Could be done with loop like so:
(loop with result = '()         ; one-time initialization
for i downfrom 10 ; count i down from 10
for j from 0 by 2 ; count j up from 0 by 2
while (< j 10) ; stop when j >= 10
do
(push (+ i j) result) ; fast-accumulate i+j
finally
return (nreverse result)) ; reverse and return result

It's a bit more verbose, but loop has a lot of options, so you want it to be reasonably transparent.

Notice that this loop declares the result array and then "returns" it. It could also operate on a variable declared outside the loop, in which case we wouldn't need the finally return clause.

The loop macro is astoundingly flexible. Its full specification is way out of scope for this primer, but if you want to make Emacs Lisp your, uh, friend, then you should spend some time reading up on loop.

for..in

If you're iterating over a collection, Java provides the "smart" for-loop, and JavaScript has for..in and for each..in. There are various ways to do it in Lisp, but you really might as well just learn how to do it with the loop macro. It's a one-stop shop for iteration.

The basic approach is to use loop for var in sequence, and then do something with the individual results. You can, for instance, collect them (or a function on them) into a result list like so:
(loop for i in '(1 2 3 4 5 6)
collect (* i i)) ; yields (1 4 9 16 25 36)

The loop macro lets you iterate over list elements, list cells, vectors, hash-keys, hash-values, buffers, windows, frames, symbols, and just about anything else you could want to traverse. See the Info pages or your Emacs manual for details.

functions

You define a function with defun.

Syntax: (defun function-name arg-list [optional docstring] body)
(defun square (x)
"Return X squared."
(* x x))

For a no-arg function, you use an empty list:
(defun hello ()
"Print the string `hello' to the minibuffer."
(message "hello!"))

The body can be any number of expressions. The return value of the function is the result of the last expression executed. You do not declare the return type, so it's useful to mention it in the documentation string. The doc string is available from M-x describe-function after you evaluate your function.

Emacs Lisp does not have function/method overloading, but it supports optional and "rest" parameters similar to what Python and Ruby offer. You can use the full Common Lisp specification for argument lists, including support for keyword arguments (see the defstruct section below), if you use the defun* macro instead of defun. The defun* version also lets you (return "foo") without having to set up your own catch/throw.

If you want your function to be available as a M-x command, put (interactive) as the first expression in the body after the doc string.

local variables

You declare function local variables with the let form. The basic syntax is (let var-decl var-decl)

(let ((name1 value1)
(name2 value2)
name3
name4
(name5 value5)
name6
...))

Each var-decl is either a single name, or (name initial-value). You can mix initialized and uninitialized values in any order. Uninitialized variables get the initial value nil.

You can have multiple let clauses in a function. Code written for performance often collects all declarations into a single let at the top, since it's a bit faster that way. Typically you should write your code for clarity first.

reference parameters

C++ has reference parameters, which allow you to modify variables from the caller's stack. Java does not, so you have to work around it occasionally by passing in a 1-element array, or using an instance variable, or whatever.

Emacs Lisp does not have true reference parameters, but it has dynamic scope, which means you can modify values on your caller's stack anyway. Consider the following pair of functions:
(defun foo ()
(let ((x 6)) ; define a local (i.e., stack) variable x initialized to 6
(bar) ; call bar
x)) ; return x

(defun bar ()
(setq x 7)) ; finds and modifies x in the caller's stack frame

If you invoke (foo) the return value is 7.

Dynamic scoping is generally considered a bad design bordering on evil, but it can occasionally come in handy. If nothing else, it's good to know it's what Emacs does.

return

A lisp function by default returns the value of the last expression executed in the function. Sometimes it's possible to structure your function so that every possible return value is in a "tail position" (meaning the last expression out before the door closes, so to speak.) For instance:

Emacs Lisp JavaScript
(require 'calendar)

(defun day-name ()
(let ((date (calendar-day-of-week
(calendar-current-date))))
(if (= date 0)
"Sunday"
(if (= date 6)
"Saturday"
"weekday"))))
function dayName() {
var date = new Date().getDay();
switch (date) {
case 0:
return "Sunday";
case 6:
return "Saturday";
default:
return "weekday";
}
}

The return value is just the result of the last expression, so whatever our nested if produces is automatically returned, and there's no need here for an explicit return form.

However, sometimes restructuring the function this way is inconvenient, and you'd prefer to do an "early return".

You can do early returns in Emacs Lisp the same way you do break and continue, using the catch/throw facility. Usually simple functions can be structured so you don't need this – it's most often useful for larger, deeply-nested functions. So for a contrived example, we'll just rewrite the function above to be closer to the JavaScript version:
(defun day-name ()
(let ((date (calendar-day-of-week
(calendar-current-date)))) ; 0-6
(catch 'return
(case date
(0
(throw 'return "Sunday"))
(6
(throw 'return "Saturday"))
(t
(throw 'return "weekday"))))))

Obviously using catch/throw here is slow and clunky compared to the alternatives, but sometimes it's exactly what you need to get out of a deeply nested construct.

try/catch

We've already discussed catch/throw, an exception-like facility for normal control flow transfers.

Emacs has a different facility for real error conditions, called the "conditions" system. Going through the full system is out of scope for our primer, but I'll cover how to catch all exceptions and how to ignore (squelch) them.

Here's an example of a universal try/catch using the condition-case construct, with a Java equivalent:


Emacs Lisp Java
(condition-case nil
(progn
(do-something)
(do-something-else))
(error
(message "oh no!")
(do-recovery-stuff)))
try {
doSomething();
doSomethingElse();
} catch (Throwable t) {
print("uh-oh");
doRecoveryStuff();
}

If you want an empty catch block (just squelch the error), you can use ignore-errors:
(ignore-errors
(do-something)
(do-something-else))

It's sometimes a good idea to slap an ignore-errors around bits of elisp code in your startup file that may not always work, so you can still at least start your Emacs up if the code is failing.

The condition-case nil means "Don't assign the error to a named variable." Elisp lets you catch different kinds of errors and examine the error data. You can read the Emacs manual or Info pages to learn more about how to do that.

The progn is necessary if you have multiple expressions (in C/Java, statements) to evaluate in the condition-case body.

condition-case will not catch values thrown by throw – the two systems are independent.

try/finally

Emacs has a "finally"-like facility called unwind-protect.


Emacs Lisp Java
(unwind-protect
(progn
(do-something)
(do-something-else))
(first-finally-expr)
(second-finally-expr))
try {
doSomething();
doSomethingElse();
} finally {
firstFinallyExpr();
secondFinallyExpr();
}

Like condition-case, unwind-protect takes a single body-form followed by one or more cleanup forms, so you need to use progn if you have more than one expression in the body.

try/catch/finally

If you make the condition-case (which is basically try/catch) the body-form of an unwind-protect (which is basically try/finally), you get the effect of try/catch/finally:
(unwind-protect                 ; finally
(condition-case nil ; try
(progn ; {
(do-something) ; body-1
(do-something-else)) ; body-2 }
(error ; catch
(message "oh no!") ; { catch 1
(poop-pants))) ; catch 2 }
(first-finally-expr) ; { finally 1
(second-finally-expr)) ; finally 2 }

Classes

Emacs Lisp is not object-oriented in the standard sense: it doesn't have classes, inheritance, polymorphism and so on. The Common Lisp package includes a useful feature called defstruct that gives you some simple OOP-like support. I'll walk through a basic example.

These two declarations are essentially equivalent:

Emacs Lisp Java
(require 'cl)  ; top of file  

(defstruct person
"A person structure."
name
(age 0)
(height 0.0))
/* A Person class */
class Person {
String name;
int age;
double height;
public Person() {}
public Person(String name) {
this(name, 0, 0);
}
public Person(int age) {
this(null, age, 0);
}
public Person(double height) {
this(null, 0, height);
}
public Person(String name, int age) {
this(name, age, 0);
}
public Person(String name, double height) {
this(name, 0, height);
}
public Person(int age, double height) {
this(null, age, height);
}
public Person(String name, int age, double height) {
this.name = name;
this.age = age;
this.height = height;
}
}

Both create a "class" with three named fields, and constructors for initializing any subset of the fields. With defstruct you get one constructor with keyword parameters, so these are all valid:
(make-person)  ; new Person()
(make-person :age 39) ; new Person(39)
(make-person :name "Steve" :height 5.83 :age 39) ; new Person("Steve", 39, 5.83)

The defstruct macro supports single-inheritance (to arbitrary depth):

Emacs Lisp Java
(defstruct (employee
(:include person))
"An employee structure."
company
(level 1)
(title "n00b"))
/* An Employee class */
class Employee extends Person {
String company;
int level = 1;
String title = "n00b";
public Employee() {
}
public Employee(String name,
String company) {
super(name);
this.company = company;
}
public Employee(String name,
int age,
String company) {
super(name, age);
this.company = company;
}
public Employee(String name,
int age,
double height,
String company) {
super(name, age, height);
this.company = company;
}
public Employee(String name,
int age,
String company,
int level) {
super(name, age);
this.company = company;
this.level = level;
}
public Employee(String name,
int age,
String co,
int lvl,
String title) {
super(name, age);
this.company = co;
this.level = lvl;
this.title = title;
}
// (remaining 150 overloaded constructors elided for brevity)
}

The defstruct macro provides a flexible default constructor, but also gives you a fair amount of control over your constructor(s) if you prefer.

The defstruct macro creates an instanceof-like predicate function named after the struct, so you can say:
(person-p (make-person))
t
(employee-p (make-person))
nil
(employee-p (make-employee))
t
(person-p (make-employee)) ; yes, it inherits from person
t
Java may suck at declaring constructors, but Emacs Lisp makes up for it by sucking at setting fields. To set a field in a struct, you have to use the setf function, and construct the field name by prepending the structure name. So:


Emacs Lisp Java
(setq e (make-employee))
(setf (employee-name e) "Steve"
(employee-age e) 39
(employee-company e) "Google"
(employee-title e) "Janitor")
Employee e = new Employee();
e.name = "Steve";
e.age = 39;
e.company = "Google";
e.title = "Janitor";

The Lisp one doesn't look too bad here, but in practice (because Elisp has no namespace support and no with-slots macro), you wind up with long structure and field names. So your defstruct-enabled elisp code tends to look more like this:
(setf (js2-compiler-data-current-script-or-function compiler-data) current-script
(js2-compiler-data-line-number compiler-data) current-line
(js2-compiler-data-allow-member-expr-as-function-name compiler-data) allow
(js2-compiler-data-language-version compiler-data) language-version)
So it goes.

To fetch the value of a field in a struct variable, you concatenate the struct name with the field name and use it as a function call:
(person-name steve)  ; yields "Steve"
There's more that defstruct can do – it's a pretty decent facility, all things considered, though it falls well short of a full object system.

Buffers as classes

In Elisp programming it can often be useful to think of buffers as instances of your own classes. This is because Emacs supports the notion of buffer-local variables: variables that automatically become buffer-local whenever they are set in any fashion. They become part of the scope chain for any code executing in the buffer, so they act a lot like encapsulated instance variables.

You can use the function make-variable-buffer-local to declare a variable as buffer-local. Usually it comes right after the defvar or defconst declaration (see below.)

Variables

You can declare a variable, optionally giving it some runtime documentation, with defvar or defconst:
(defconst pi 3.14159 "A gross approximation of pi.")
The syntax is (defvar name value [ doc-string ]).

Ironically, defconst is variable and defvar is constant, at least if you re-evaluate them. To change the value of a defvar variable by re-evaluating its declaration you need to use makunbound to unbind it first. You can always change the value of any defvar or defconst variable using setq. The only difference between the two is that defconst makes it clearer to the programmer that the value is not intended to change.

You can use setq to create brand-new variables, but if you use defvar, the byte-compiler will be able to catch more typos.

Further reading

Emacs Lisp is a real programming language. It has a compiler, a debugger, a profiler, pretty-printers, runtime documentation, libraries, I/O, networking, process control and much more. There's a lot to learn, but I'm hoping this little primer has got you over the hump, as it were.

In spite of its various quirks and annoyances, Elisp is reasonably fun to program in once you get the hang of it. As a language it's not that great, and everyone wishes it were Common Lisp or Scheme or some other reasonable Lisp dialect. Some people even wish it weren't Lisp at all, if you can believe that! (hee)

But it's really, really useful to be able to customize your editor, and also to be able to fix problems with elisp code you borrowed or inherited. So a little Elisp goes a long way.

For those of you learning Emacs Lisp, please let me know if you found this useful. If you try writing some Emacs extensions, let me know what you would like to see documented next; I can always do another installment of the Emergency Elisp series if there's enough interest.

Good luck!

49 Comments:

Blogger Ben said...

Just what the doctor ordered, and far more useful than the FSF book. Thanks!

2:59 PM, January 24, 2008  
Blogger Mitchell said...

So are you refuting your own "Lisp is not an acceptable Lisp" argument here by trying to steer new people into getting some of it on themselves?

3:06 PM, January 24, 2008  
Blogger Chris said...

Brilliant ! Look forward to studying it in detail. Thank you. I'd love to see you go through an existing elisp package and show how it works ... M-x occur, for example.

3:42 PM, January 24, 2008  
Blogger offby1 said...

(quote 1 2 3)

doesn't work; you meant

(quote (1 2 3))

3:51 PM, January 24, 2008  
Blogger Deepak said...

Just what I needed as I attempt to walk down the Emacs path once again. Thanks a lot!

4:13 PM, January 24, 2008  
Blogger hugo said...

Wow! I've been customizing emacs for 2 years and hadn't heard about: hex and binary notation, try/catch, catch/throw, do/while, case, defstruct and makunbound!

4:19 PM, January 24, 2008  
Blogger Jonas said...

#xdeadbeef => -22167825

when you only have 29 bits...

4:41 PM, January 24, 2008  
Blogger Jeramey said...

Now if elisp would just get decent file stream support! Everything-is-a-buffer isn't nearly so elegant and useful as everything-is-a-byte-stream. Or maybe I've just been using Unix too long and it has poisoned my soul.

4:57 PM, January 24, 2008  
Blogger Steve Yegge said...

Ben: FSF book will seem more useful after you absorb this, I think.

Mitchell: we have to live with (and work with) what we've got.

Offby1: thanks, fixed.

Hugo: no kidding. There's a lot under the covers.

Jonas: thanks, changed to #xdeadbee :-)

5:22 PM, January 24, 2008  
Blogger Jonathan Watmough said...

Lisp is great fun once you get into it.

There's an open source JVM based Lisp called Clojure, with an emphasis on functional programming, but also STM and great concurrency support from the JVM.

Check it out at clojure.sourceforge.net

I honestly haven't had as much fun programming since BBC-Basic when I was a nipper. ;-)

5:47 PM, January 24, 2008  
Blogger David Plumpton said...

The programming tips are fine, but how about a follow-up article on things like creating new editing modes, adding menus, making hot-keys do useful things, etc. In other words driving Emacs to do useful stuff.

6:04 PM, January 24, 2008  
Blogger hugo said...

... writing a javascript byte-compiler, etc. (js2? no name yet i guess)

But if you are going to talk more about elisp, don't forget to mention how you can easily tweak other people code with hooks and advices.

And btw, why are you reading the comments?

6:11 PM, January 24, 2008  
Blogger Eric said...

And I quote -

(if (today-is-friday)
(message "yay, friday")
(message "not friday!")
(non-friday-stuff)
(more-non-friday-stuff)

Someone forgot a paren. Not using emacs to edit this post I see :p

8:15 PM, January 24, 2008  
Blogger Josh said...

Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you!

I've been using emacs for 12+ years and must admit lisp has always been intimidating to me. This post really clears up the basic syntax.

8:17 PM, January 24, 2008  
Blogger Matt said...

Quite handy. Thanks, Steve.

8:56 PM, January 24, 2008  
Blogger kalid said...

Thanks for the post! I was just reading the official emacs Lisp manual (http://www.gnu.org/software/emacs/manual/html_mono/elisp.html) but this is much more practical and concise.

9:04 PM, January 24, 2008  
Blogger piyo said...

Thank you for this post. It's a useful summary and reminder.

About dynamic scope, you should at least mention its good for mocking and overriding.

> (require 'cl)

Yes! Too bad ""(require 'cl) considered harmful" considered harmful" is down. :-)

I suspect you are trying to build the crowd to the point when you switch over to your JavaScript emitter to elisp bytecode. "Now this is how you do it right." :)

1:27 AM, January 25, 2008  
Blogger dandavat said...

Excellent elisp reference for beginners. Thanks!

You missed a closing code tag near: "(let var-decl var-decl".

I think the most popular topic is how to build a major mode for emacs, or to fix/contribute to an existing mode. There are some useful resources on this topic, but I believe you can present it much better.

3:20 AM, January 25, 2008  
Blogger Kelly said...

Steve, I love your posts - they always brighten my day. I would love to see more emergency elisp! Other than the built in manuals, is there a resource that you have found particularly helpful?

btw (incf 13 6) won't work - it has to be something like (let ((x 13)) (incf x 6)) just in case you get around to creating a book out of this (hint hint)

3:42 AM, January 25, 2008  
Blogger John said...

Great article. Just wanted to let you know that the second half of the article is hard to read from IE but looks fine in FireFox. Near the top of your section on local variables, there's an opening code tag in the HTML that isn't closed. As a result, the bottom half of the article is in green Courier font under IE.

7:11 AM, January 25, 2008  
Blogger Doug said...

+1 on what David Plumpton said.

This was a nice intro to lisp, but I have done a bit of lisp in the past.

What would be useful for me is an intro to the emacs specific stuff, such as commonly used functions used on regions, "save-excursion", option to (interactive), what emacs modes do (from a code point of view).

What are the elisp constructs you find in your own code most often?

10:09 AM, January 25, 2008  
Blogger rfunk said...

Nice! I've been using Emacs for 15 years and have read the O'Reilly Emacs Extensions book, but at least half of this was new to me. I'd love to see more elisp tutorials!

BTW, The missed closing code tag after the let example also breaks (half of) the article in Konqueror.

10:15 AM, January 25, 2008  
Blogger dm said...

following on from John a few posts above: the missing code tag also breaks the rendering in Opera, "the standards compliant browser"
;)

mind you the mono-spaced font is a nice change from the usual

10:40 AM, January 25, 2008  
Blogger Steve Yegge said...

Thanks for all the corrections - I've incorporated them into the post, plus a few that nobody caught.

12:51 PM, January 25, 2008  
Blogger Youssef ELatia said...

Keep going. Very very helpful.

And change the blog_check to something like 10000 instead of 5000.

Whenever I finish reading your essays , I feel like I need to read more.

7:16 PM, January 25, 2008  
Blogger Liron said...

single-line comments only?!

#|
Lisp
has
read
macros
Steve!
|#

8:00 PM, January 25, 2008  
Blogger Steve Yegge said...

#|
Liron
Can't
Fugging
Read
|#

This article was about Emacs Lisp, which does not have read macros (although I wish it did.)

Thanks for playing.

11:30 PM, January 25, 2008  
Blogger Cliff said...

+1 on what John said, I get green mono spaced font in Safari too.

Otherwise excellent post. Thank You.

12:23 AM, January 26, 2008  
Blogger Xah Lee said...

Hi Steve,

Nice tutorial.

I have also wrote one similar:
http://xahlee.org/emacs/elisp_basics.html

There are few differences than yours. Here's the major ones:

• i wouldn't go into octal, hex, binary literal notations. In my 20 years of computing in various lang, i don't think i've used them once. (my area is web app dev, unix sys admin, geometry programing; using mostly high level or scripting langs)


• Your mention of bit operator “<<” seems especially odd to me.
In your example:
“To compute the arithmetic expression (0x15 * (8.2 + (7 << 3))) % 2:
(% (* #x15 (+ 8.2 (lsh 7 3))) 2)”

I scratched my head. I had to look up what is “<<” or “lsh”. Again, personally, i've never needed to tweak bits.

• i wouldn't tell my readers to use the “*scratch*” buffer, because i consider it quaint and a obsticle to spreading emacs. Anyway, the C-j in scratch buffer is new to me. I would just tell reader to open a new file and do “Alt+x emacs-lisp-mode”.

• the use of comma here is new to me:
“`(1 ,(+ 1 1) 3)”
However, in that sentence, you said “.. via a template system called "backq”
Just realized that in Opera, much of your code is cut off and invisible.
Only when pasted, i see the word “backquote”. But in Opera browser, i see just “"back” with no indication whatsoever that it has been cut off or needs to scroll. Now i realized other parts of your lisp code also got cut. This is latest Opera on OSX 10.4.11.

• The equality treatment is a bit complex too for a primer. I'd just mention “equal” and “eq”.

• The “apropos” is new to me! (i use emacs daily since 1998) I've already used “C-h a” (apropos-command) or “C-u C-h a”. But i think “apropos” is more versatile since it searching lisp symbols (as opposed to just functions, commands, or variables). Did you mention “C-h f” (describe-function) somewhere? Also i think “Alt+x elisp-index-search” is very useful to me.

• for a simple primer, why mention the so many variation of switch and cond? Just if, if else, cond, will be good for 99% of coding needs i'm sure. The minor variations are fluffy.

• for a simple primer, why load the controversial cl? and cover all the loop forms? To me, “while” and “mapcar” basically does cover 99% needs. If not, then there's “dolist”, “dotimes”, all are cannonical elisp.

• Thanks for covering break/continue. I haven't used it before. It is good learning for me personally.

• you wrote:
«You can use the full Common Lisp specification for argument lists, including support for keyword arguments (see the defstruct section below), if you use the defun* macro instead of defun. The defun* version also lets you (return "foo") without having to set up your own catch/throw.»

I didn't understand this paragraph. (am not familiar with CL) Egads, why are you keep mentioning Common Lisp's concepts in this _introductory_ tutorial? I think emacs lisp is novel enough to keep lisp newbies busy.

When i learn a new system, i tend to want to only learn the pure form, without any add on or modifications. Once i'm well familiar with the raw form, then i study the add ons and variations etc.

• Your section on “reference parameters” is confusing to me. For someone who does not have compiler knowledge, any mentioning of “stack” is confusing. I understand full well of dynamic scope, as well much advanced mathematics in logic. I don't understand what behavior in that section you are trying to say, other than giving a example of dynamic scope of elisp.

What computational effect/behavior is it trying to illustrate? What concrete problem is this “reference parameter” or “modify caller's stack” is supposed to solve? I'd frame this section in these respects. (as opposed to, using the term stack, call-back, reference... which i honestly can't interpret sensibly.)

• in elisp, when would i want to use a class such as defstruct? (as opposed to the built-in alist or hash table)
Personally, i never find OOP paradigm useful, so this is a geniun question.

• treating buffers in a primer is probably complex. Especially treating it as a data structure of “class”, with quite a lot complexities of the local var.

I'd include some real useful examples of little elisp functions you actually use to help you code or use emacs.
(my version is here: http://xahlee.org/emacs/elisp_examples.html ), or start a introduction that covers on how to actually do text processing in emacs, covering functions and concepts such as point, region-max, region-beginning, goto-char, search-forward, delete-char, insert ...
(my version here: http://xahlee.org/emacs/elisp_editing_basics.html )

All the above is just personal rambling. Mostly written just for my own pleasure. :D

Xah
xah@xahlee.org
∑ http://xahlee.org/

8:13 AM, January 26, 2008  
Blogger Amund Tveit said...

Great elisp summary. Next one about pymacs ?

9:01 AM, January 26, 2008  
Blogger Chris Barts said...

Are elisp macros broken, or did you think an introduction to macros would be out of place here? If the second, please consider a follow-up post focused on macros.

1:16 AM, January 27, 2008  
Blogger jrockway said...

Chris: elisp macros are fine, and he did mention the backquote operator (usually used to build macros). It is a rather complicated topic, though, so it makes sense to omit it. M-: (info "elisp") and enjoy learning the rest.

3:35 AM, January 27, 2008  
Blogger plh said...

There's a with-slots for structs in SLIME's slime.el:

(defmacro* with-struct ((conc-name &rest slots) struct &body body)
"Like with-slots but works only for structs.
\(fn (CONC-NAME &rest SLOTS) STRUCT &body BODY)" ... )

5:53 AM, January 27, 2008  
Blogger Tombo said...

I wouldn't normally ask, but if you're looking for things beginners find hard:

I'm writing a major mode for some language and keep fiddling around with the font-lock settings. I want to "unload" the current definition of my mode, hack the regexps, load the new definition and re-fontify a buffer full of the language I'm trying to write the mode for. I've tried using load-library, unload-feature, and font-lock-fontify-buffer, making sure to switch into fundamental mode before I unload-feature, but no joy - the font-lock defs don't seem to change till I restart emacs.

Great tutorial BTW - and I'm off to read up about CL loops now.

10:09 AM, January 27, 2008  
Blogger Scott Frazer said...

With regard to defvar, you don't have to makunbound to re-evaluate them, you can use C-M-x (which maps to eval-defun).

Tombo: This applies to your problem with font-locking (probably). You need to C-M-x your defvar'd font-lock variables after you change the regexps. Since they are buffer-local, you will need to close the file and then reopen it to see the results, but you certainly don't need to restart Emacs.

5:43 AM, January 28, 2008  
Blogger Kelsin said...

I would also appreciate more emacs related stuff (For example,I often have different file formats that I would love to know how to make a quick mode for font-locking them.) This post is definitely a bookmark though!

9:32 AM, January 28, 2008  
Blogger j said...

Steve,

Thanks for this post - I feverishly devour all of your emacs/LISP/eLISP posts as I strive to increase my productivity with emacs.

I only wish LISP enjoyed wider adoption as a programming language.

12:00 AM, January 29, 2008  
Blogger Glenatron said...

A really helpful starting point, thanks.

Now I just need to know whic emacs-lisp function will tell me that the code I'm writing is totally duplicating something that it does already but I didn't know about.

4:01 AM, January 30, 2008  
Blogger Justin D-Z said...

I'm probably the 37th person to try to give you this link today:

http://xkcd.com/378/

Funny web comic addresses Emacs.

4:43 AM, February 01, 2008  
Blogger 风灵轩 said...

I love it!

7:19 PM, February 04, 2008  
Blogger Kzar said...

Cheers this was a handy post. I think macros would be a good topic if you do another one.

11:40 AM, February 08, 2008  
Blogger jrockway said...

One thing that people may like is M-x ielm, it's a REPL for elisp. lisp-interaction-mode does the same thing, but ielm feels more like the perl/python/ruby REPLs that everyone is probably used to.

10:37 PM, February 10, 2008  
Blogger Rocky said...

Somewhere in one of your excellent blogs I recall something about the annoyance of how the escaping of paren's and or's in Emacs Lisp work backwards from other programming languages. Below is some code I've been using for a little while to reverse this. It seems to work well enough for my purposes.

There's a little duplication in this code, but I haven't decided to take the plunge and go to the next step which would be to change the inner function of query-replace-regexp and friends and and this to perform-string. After this code is a little unit test I use to check things.

P.S. Sorry about the funny formatting. I can't figure out how to tag this as program code or verbatim, monospace text.

;; Call this file say rubyre.el
(defun gsub (search-string replace string &optional regexp-flag)
"Like Ruby gsub."
(with-temp-buffer
(insert string)
(goto-char (point-min))
(let ((search-function (if regexp-flag 're-search-forward 'search-forward)))
(while (funcall search-function search-string nil t)
(replace-match replace))
(buffer-string))))

(defun re-flip (search-string string)
"Flip backslashed character and nonbackslashed version. E.g \(ab\)-> (ab)
while (a|b) -> (ab) -> \(ab\)"
(with-temp-buffer
(let ((pair-string (concat "\\(.\\)\\(" search-string "\\)")))
(insert (concat "X" string)) ;; to allow matching the initial character
(goto-char (point-min))
(while (re-search-forward pair-string nil t)
(if (string= "\\" (match-string 1))
(replace-match (match-string 2))
(replace-match (concat (match-string 1) "\\\\" (match-string 2)))))
(substring (buffer-string) 1))))

(defun ruby2elisp (regexp)
"Convert a Ruby style regexp to an elisp regexp."
(setq regexp (gsub "\\\\d" "[0-9]" regexp t))
(setq regexp (gsub "\\\\D" "[^0-9]" regexp t))
(setq regexp (gsub "\\\\s" "[ \n\t]" regexp t))
(setq regexp (gsub "\\\\S" "[^ \n\t]" regexp t))
(setq regexp (re-flip "[(){}|]" regexp)))

(defun rreplace-regexp (regexp to-string &optional delimited start end)
(interactive
(let ((common
(query-replace-read-args
(if (and transient-mark-mode mark-active)
"Replace regexp in region"
"Replace regexp")
t)))
(list (nth 0 common) (nth 1 common) (nth 2 common)
(if (and transient-mark-mode mark-active)
(region-beginning))
(if (and transient-mark-mode mark-active)
(region-end)))))
(perform-replace (ruby2elisp regexp) to-string nil t delimited nil nil
start end))

(defun rre-search-forward (regexp &optional bound noerror count)
(interactive "sRuby RE search: ")
(re-search-forward (ruby2elisp regexp) bound noerror count))

(defun rre-search-backward (regexp &optional bound noerror count)
(interactive "sRuby RE search: ")
(re-search-backward (ruby2elisp regexp) bound noerror count))


;;; New file
;; -*- emacs-lisp -*-
;; This program has to be run from the directory it is currently in and
;; the rdebug code has to be in the parent directory
(load-file "./elk-test.el")
(load-file "./rubyre.el")

(deftest "gsub-test"
(assert-equal "BBBCD" (gsub "A" "B" "ABACD"))
(assert-equal "BBD" (gsub "A." "B" "ABACD" t))
(assert-equal "FFFCD" (gsub "[AB]" "F" "ABACD" t))
(assert-equal "ABAFF" (gsub "[^AB]" "F" "ABACD" t))
(assert-equal "A\\|B" (gsub "\\([^\\]\\)|" "\\1\\\\|" "A|B" t)))

(deftest "flip-test"
(assert-equal "\\(ab)" (re-flip "(" "(ab)"))
(assert-equal "(ab\\)" (re-flip ")" "(ab)"))
(assert-equal "\\(ab)" (re-flip "(" "\(ab)"))
(assert-equal "(ab\\)" (re-flip ")" "(ab\)"))
(assert-equal "\\(ab\\)" (re-flip "[(){}]" "(ab)")))
(assert-equal "(ab)" (re-flip "[(){}]" "\\(ab\\)"))

;; -------------------------------------------------------------------
;; Build and run the test suite.
;;

(build-suite "rubyre-suite" "gsub-test" "flip-test")
(run-elk-test "rubyre-suite"
"test things in rubyre.el")

11:28 AM, February 20, 2008  
Blogger Paul said...

Steve,
What emacs mode/tools do you use for editing html and managing this blog?

7:31 AM, March 23, 2008  
Blogger Anxious Mo-Fo said...

This is incredibly useful. Thank you very much!

10:41 AM, July 25, 2008  
Blogger xradiographer said...

After a year+ of Emacs, I'm still very much an elisp beginner. I've referred to this page several times.

Keep up the emacs work (wether it's ECMA- or EMACs- -script!

11:58 AM, July 27, 2008  
Blogger an0 said...

> The defun* version also lets you (return "foo") without having to set up your own catch/throw.»

I think what you really want to say here is the function body of defun* is implicitly surrounded by (block function-name ...).
In fact, you can not (return "foo"), but can (return-from function-name "foo").

8:36 PM, September 04, 2008  
Blogger Paul Hobbs said...

This is VERY useful. Thank you so much!

2:07 PM, April 12, 2010  
Blogger math0ne said...

Great post man, just got heavily into emacs and this helped fill in a number of blanks for me.

7:03 PM, October 05, 2010  

Post a Comment

<< Home