Emacs Lisp Cheat Sheet for Clojure Developers
Tags: emacs, cheatsheet, clojure
Published: 2023-08-04 10:55:30

---
title: Emacs Lisp Cheat Sheet for Clojure Developers
description: If you're a Clojure dev looking to learn Elisp, here's a concise guide which focuses on the main differences between the two languages.
canonical_url: https://robjohnson.dev/posts/elisp-cheat-sheet-for-clojure-devs/
tags: #emacs #cheatsheet #clojure
published: true
---

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.
<!-- markdown-toc start - Don't edit this section. Run M-x markdown-toc-refresh-toc -->
**Table of Contents**

- [Numeric types](#numeric-types)
- [Character types](#character-types)
- [Conditional logic](#conditional-logic)
- [Equality](#equality)
- [Functions](#functions)
- [Let forms](#let-forms)
- [Comments](#comments)
- [Namespaces and access modifiers](#namespaces-and-access-modifiers)
- [Variables](#variables)
- [Symbols, values & lambdas](#symbols-values--lambdas)
- [Threading macros & higher-order functions](#threading-macros--higher-order-functions)
- [False friends](#false-friends)
    - [`concat`](#concat)
    - [`string`](#string)
- [Aliases](#aliases)
    - [Suggested aliases](#suggested-aliases)
- [Going further](#going-further)
- [Acknowledgements](#acknowledgements)

<!-- markdown-toc end -->


### Numeric types

Elisp has integers and floats. These can be mixed in arithmetic functions how you'd usually expect:

```elisp
(+ 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:

```elisp
(/ 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):

```elisp
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:

```elisp
1e2 ; => 100.0
```

Integers can be arbitrarily large;


```elisp
(+ 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:

```elisp
"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:

```elisp
(string ?f ?o ?o) ; => "foo"
```

Characters are in fact simply integers:

```elisp
?A ; => 65 (#o101, #x41, ?A)
(string 102 111 111) ; => "foo"
(+ ?f ?o) ; => 213 (#o325, #xd5)
```

Though not all integers are valid characters:

```elisp
(string 12356789) ; => error!
```

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

```elisp
(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.

```elisp
(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:

```elisp
(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):

```elisp
(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):
```elisp
(= 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:

```elisp
(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.

```elisp
(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):

```elisp
(eq "a" "a") ; => nil
```
(See [Comparison Functions](https://www.emacswiki.org/emacs/ComparisonFunctions) on the Emacs Wiki for further info.)

### Functions

Functions are defined using `defun`. The arguments are enclosed in a parens (rather than in brackets):

```elisp
(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.

```elisp
(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`:

```elisp
(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:

```elisp
(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:

```elisp
(let* ((x 1)
       (y (+ 1 x)))
  (* 3 y)) ; => 6
```

`let` also exists, but it doesn't let you use earlier bindings within later ones:

```elisp
(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](https://practical.li/clojure/reference/clojure-syntax/comments/#comment-forms-with-the-comment-reader-macro) ( `#_` )
- There isn't a built-in ["Rich comment" block
  macro](https://practical.li/clojure/reference/clojure-syntax/comments/#comment-forms-with-the-comment-reader-macro) -
  BUT thankfully you can create one with a macro:

```elisp
(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"):

```elisp
(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`:

```elisp
(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**:

```elisp
(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:

```elisp
;; 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:

```elisp
(let* ((times-2 (lambda (x) (* 2 x))))
  (times-2 3))
;; => error: (void-function times-2)
```

Instead, you need to use `funcall`:

```elisp
(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](https://github.com/magnars/dash.el):

```elisp
;; (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:

```elisp
(concat "foo" "bar")
;; => "foobar"

(concat '(65 66) '(67 68))
;; => "ABCD"
```

#### `string`

Elisp's `string` function - unlike Clojure's `str` - only works on characters:

```elisp
(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):

```elisp
(string 65) ; => "B"

(string 123456789) ; => error!
```
### Aliases

You can create aliases using `defalias`:

```elisp
(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:

```elisp
(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:

```elisp
(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](https://robjohnson.dev/posts/everyday-editor-extensions-in-emacs/). The [Awesome Elisp](https://github.com/p3r7/awesome-elisp) GitHub repo is also a great resource.

### Acknowledgements

Thanks to [ambirdsall](https://www.reddit.com/user/ambirdsall/), [hvis](https://www.reddit.com/user/hvis/) & Philip Kaludercic for suggestions. See [Reddit](https://www.reddit.com/r/emacs/comments/155upms/emacs_lisp_cheat_sheet_for_clojure_developers/) for discussion.