DEV Community

Robert Johnson
Robert Johnson

Posted on • Originally published at robjohnson.dev

Emacs Lisp Cheat Sheet for Clojure Developers

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

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
Enter fullscreen mode Exit fullscreen mode

It doesn't have Clojure's ratio type though, so integer division works similarly to most other languages:

(/ 3 2) ; => 1
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

Floats can be expressed in scientific notation:

1e2 ; => 100.0
Enter fullscreen mode Exit fullscreen mode

Integers can be arbitrarily large;

(+ 1 10000000000000000000)
; => 10000000000000000001
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

Character literals a displayed using a ? prefix:

(string ?f ?o ?o) ; => "foo"
Enter fullscreen mode Exit fullscreen mode

Characters are in fact simply integers:

?A ; => 65 (#o101, #x41, ?A)
(string 102 111 111) ; => "foo"
(+ ?f ?o) ; => 213 (#o325, #xd5)
Enter fullscreen mode Exit fullscreen mode

Though not all integers are valid characters:

(string 12356789) ; => error!
Enter fullscreen mode Exit fullscreen mode

You can format strings much the same as in Clojure using the format function:

(format "I have %s bananas" 3)
;; => "I have 3 bananas"
Enter fullscreen mode Exit fullscreen mode

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")
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

(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!
Enter fullscreen mode Exit fullscreen mode

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!
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

eq Performs reference-based equality (like Clojure's identical fn):

(eq "a" "a") ; => nil
Enter fullscreen mode Exit fullscreen mode

(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))
Enter fullscreen mode Exit fullscreen mode

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))
Enter fullscreen mode Exit fullscreen mode

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)"
Enter fullscreen mode Exit fullscreen mode

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!"
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Comments

(defmacro comment (&rest body)
  nil)

(comment
 (blow-up-the-world))
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

We can create anonymous functions using lambda - but because lambdas are stored in variables rather than defuns, you can't call them directly:

(let* ((times-2 (lambda (x) (* 2 x))))
  (times-2 3))
;; => error: (void-function times-2)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)))
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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!
Enter fullscreen mode Exit fullscreen mode

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!
Enter fullscreen mode Exit fullscreen mode

Aliases

You can create aliases using defalias:

(defun foo (x y)
  (+ x y))
(defalias 'bar #'foo)

(foo 3 1) ; => 4
(bar 3 1) ; => 4
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Suggested aliases

Here are some Clojure-style aliases for their Emacs builtin equivalents:

(defalias 'do 'progn)
(defalias 'when-not 'unless)
Enter fullscreen mode Exit fullscreen mode

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)