If you're a Clojure dev and you're looking to learn Elisp, you have a great head start since you know a lisp dialect already. Here's a concise guide which focuses on the main differences between the two languages.
Table of Contents
- Numeric types
- Character types
- Conditional logic
- Equality
- Functions
- Let forms
- Comments
- Namespaces and access modifiers
- Variables
- Symbols, values & lambdas
- Threading macros & higher-order functions
- False friends
- Aliases
- Going further
- Acknowledgements
Numeric types
Elisp has integers and floats. These can be mixed in arithmetic functions how you'd usually expect:
(+ 1 1) ; => 2
(+ 1 1.0) ; => 2.0
(+ 1.0 1.0) ; => 2.0
It doesn't have Clojure's ratio type though, so integer division works similarly to most other languages:
(/ 3 2) ; => 1
Integers can be expressed in multiple ways; the printed representation of REPL output looks odd at first, but it's simply showing you the alternate representations of the integer (I've omitted this elsewhere for brevity):
16 ; => 16 (#o20, #x10, ?\C-p)
;; Shows decimal, octal, hex and character value
;; (see 'character types' section below)
Floats can be expressed in scientific notation:
1e2 ; => 100.0
Integers can be arbitrarily large;
(+ 1 10000000000000000000)
; => 10000000000000000001
Character types
Elisp has strings and characters. Strings are essentially arrays of characters. They are represented and escaped in much the same way as Clojure:
"This is a simple string"
"A newline character: \n"
"Escaped line breaks\
are ignored"
;; => "Escaped line breaks are ignored"
Character literals a displayed using a ?
prefix:
(string ?f ?o ?o) ; => "foo"
Characters are in fact simply integers:
?A ; => 65 (#o101, #x41, ?A)
(string 102 111 111) ; => "foo"
(+ ?f ?o) ; => 213 (#o325, #xd5)
Though not all integers are valid characters:
(string 12356789) ; => error!
You can format strings much the same as in Clojure using the format
function:
(format "I have %s bananas" 3)
;; => "I have 3 bananas"
You can also display messages (also with optional formatting elements) in the minibuffer using the message
function.
(message "Hello, world!")
(message "Show a message to %s",
"Alice")
Such messages are also sent to the *Messages*
buffer, making message
helpful for debugging.
Conditional logic
nil
and the empty list '()
are falsy; everything else is truthy:
(if nil "truthy" "falsey")
;; => "falsey"
(if '() "truthy" "falsey")
;; => "falsey"
(if "a" "truthy" "falsey")
;; => "truthy"
(if "" "truthy" "falsey")
;; => "truthy"
(if 1 "truthy" "falsey")
;; => "truthy"
(if 0 "truthy" "falsey")
;; => "truthy"
Elisp doesn't have a separate Boolean type with true
and false
values; instead, the symbol t
is used to represent true and nil
is used to represent false. Predicate functions conventionally have a p
suffix (rather than ?
as in Clojure):
(integerp 1) ; => t
(integerp 1.5) ; => nil
(Elisp will accept symbol names containing ?
, so you can name your own predicates in the Clojure style if you want.)
Equality
=
is specifically for checking comparisons on number types (integers & floats):
(= 1 1) ; => t
(= 2 2.0) ; => t
(= 3.0 3.0) ; => t
(= 1 2) ; => nil
(= 1 "a") ; => error!
string=
is for comparing strings and characters. It is case sensitive:
(string= "a" "a") ; => t
(string= "a" 'a) ; => t
(string= "a" "b") ; => nil
(string= "a" "A") ; => nil
(string= 1 1) ; => error!
equal
does value-based comparisons. It works for numbers & string types too, but it doesn't compare across ints & floats and strings & symbols like =
& string=
do. It too is case sensitive.
(equal 1 1) ; => t
(equal "a" "a") ; => t
(equal "a" "A") ; => nil
(equal 1 1.0) ; => nil
eq
Performs reference-based equality (like Clojure's identical
fn):
(eq "a" "a") ; => nil
(See Comparison Functions on the Emacs Wiki for further info.)
Functions
Functions are defined using defun
. The arguments are enclosed in a parens (rather than in brackets):
(defun foo (x y)
(+ x y))
Docstrings are a bit different in Elisp though:
- they go after the argument list
- line breaks are preserved, but so is any indentation-related whitespace; therefore the convention is to simply remove indentation for docstrings for all but the first line.
(defun foo (x y)
"This is a docstring, continuing
onto the next line"
(+ x y))
Variadic functions (AKA functions with "rest args") can be created using
&rest
:
(defun bar (x &rest r)
(format "First arg: %s; rest: %s" x r))
(bar 1 2 3)
;; => "First arg: 1; rest: (2 3)"
You can't create true multi-arity functions in Elisp, but you can get close enough by using optional arguments:
(defun greet (person1 &optional person2)
(if person2
(message "Hello, %s and %s!"
person1 person2)
(message "Hello, %s!" person1)))
(greet "Alice")
;; => "Hello, Alice!"
(greet "Alice" "Bob")
;; => "Hello, Alice and Bob!"
Let forms
For let bindings, use let*
. The bindings are placed in an (unquoted) list, and each binding pair has to be placed in its own list too:
(let* ((x 1)
(y (+ 1 x)))
(* 3 y)) ; => 6
let
also exists, but it doesn't let you use earlier bindings within later ones:
(let ((x 1)
(y (+ 1 x)))
(* 3 y)) ; => error!
(let ((x 1)
(y 2))
(* 3 y)) ; => 6
Comments
- Line comments still work just as you expect - e.g.
;; this is a comment
- There isn't a comment reader macro (
#_
) - There isn't a built-in "Rich comment" block macro - BUT thankfully you can create one with a macro:
(defmacro comment (&rest body)
nil)
(comment
(blow-up-the-world))
Namespaces and access modifiers
Emacs doesn't have namespaces, and so doesn't have access modifiers either.
By convention, packages prefix all their functions & variables with a prefix, packagename-
; e.g. all functions in the org
package are prefixed with org-
.
You do still have to require
packages in order to ensure they are loaded before using them. E.g. (require 'org)
would ensure that the code for the org
package is loaded. Depending on your emacs config / distribution, many packages may already be required for you.
Functions intended as private are by convention signified with an extra -
in the prefix; e.g. org--get-local-tags
is a private function in the org
package.
Variables
Variables can be created & updated using setq
(short for "set quoted"):
(setq my-variable 2)
(+ 1 my-variable) ; => 3
defvar
will create a variable, but won't update it if it's already been set; you can basically think of it as Clojure's defonce
:
(defvar cheese "brie")
(message "cheese = %s" cheese)
;; => "cheese = brie"
(defvar cheese "stilton")
(message "cheese = %s" cheese)
;; => "cheese = brie"
(setq cheese "stilton")
(message "cheese = %s" cheese)
;; => "cheese = stilton"
Symbols, values & lambdas
One of the more confusing parts of Elisp is that its symbols point to multiple storage locations; a single symbol can refer to both a function and a variable:
(setq baz 123)
(defun baz (x y)
(- x y))
(message "baz as value: %s" baz)
;; => "baz as value: 123"
(baz 10 3)
;; => 7
This means that most of the time when passing around functions you have to quote them:
;; Not quoting + here - will error
(apply + '(1 2 3))
;; => error: (void-variable +)
;; Quoting + here - OK
(apply '+ '(1 2 3))
;; => 6
We can create anonymous functions using lambda
- but because lambdas are stored in variables rather than defun
s, you can't call them directly:
(let* ((times-2 (lambda (x) (* 2 x))))
(times-2 3))
;; => error: (void-function times-2)
Instead, you need to use funcall
:
(let* ((inc (lambda (x) (+ x 1)))
(dec (lambda (x) (- x 1))))
(message "result for inc: %s"
(funcall inc 10))
(message "result for dec: %s"
(funcall dec 10)))
;; =>
;; result for inc: 11
;; result for dec: 9
Threading macros & higher-order functions
A lot of Clojure-style goodness can be provided by using the excellent dash package:
;; (if not installed already)
(package-install 'dash)
(require 'dash)
;; Threading macros:
(-> 10
(+ 1)
(* 2)) ; => 22
;; Higher order functions:
(let* ((inc (lambda (x)
(+ x 1))))
(-map inc '(1 2 3)))
;; => (2 3 4)
(let* ((evenp (lambda (n)
(= 0 (% n 2)))))
(-filter evenp '(1 2 3 4)))
Note that Emacs often has its own built-in versions for these - e.g. thread-first
& thread-last
for threading, mapcar
as an equivalent to Clojure's map
- but as a Clojurist you'll probably find what you're looking for quicker by using dash
's functions instead.
False friends
concat
Elisp's concat
not only concatenates but also converts everything received into a string:
(concat "foo" "bar")
;; => "foobar"
(concat '(65 66) '(67 68))
;; => "ABCD"
string
Elisp's string
function - unlike Clojure's str
- only works on characters:
(string ?f ?o ?o) ; => "foo"
(string "foo") ; => error!
(string "f") ; => error!
(string 1.0) ; => error!
It can operate on integers, but it converts them to the character corresponding to its charcode (assuming it has one):
(string 65) ; => "B"
(string 123456789) ; => error!
Aliases
You can create aliases using defalias
:
(defun foo (x y)
(+ x y))
(defalias 'bar #'foo)
(foo 3 1) ; => 4
(bar 3 1) ; => 4
NB we linked to the function cell of foo using #'
above, so we can redef foo
, and bar
will pick up the change too:
(defun foo (x y)
(- x y))
(foo 3 1) ; => 2
(bar 3 1) ; => 2
Suggested aliases
Here are some Clojure-style aliases for their Emacs builtin equivalents:
(defalias 'do 'progn)
(defalias 'when-not 'unless)
Going further
If you'd like to put some Elisp into practice, you could also check out my intro to extending Emacs using Elisp. The Awesome Elisp GitHub repo is also a great resource.
Acknowledgements
Thanks to ambirdsall, hvis & Philip Kaludercic for suggestions. See Reddit for discussion.
Top comments (0)