¶What will we cover?
We’ll get a sense for the basic data types of the language and how we can use them!
- True and false
- Numbers
- Characters
- Sequences
- Strings
- Lists
- Arrays
- Combining expressions with logic operators
- Conditional logic
- Loops
¶Follow along with IELM!
You can open up an interactive Emacs Lisp REPL in Emacs by running M-x ielm.
I’ll be using the following snippet for evaluating code in the REPL:
(defun efs/ielm-send-line-or-region () (interactive) (unless (use-region-p) (forward-line 0) (set-mark-command nil) (forward-line 1)) (backward-char 1) (let ((text (buffer-substring-no-properties (region-beginning) (region-end)))) (with-current-buffer "*ielm*" (insert text) (ielm-send-input)) (deactivate-mark))) (defun efs/show-ielm () (interactive) (select-window (split-window-vertically -10)) (ielm) (text-scale-set 1)) (define-key org-mode-map (kbd "C-c C-e") 'efs/ielm-send-line-or-region) (define-key org-mode-map (kbd "C-c E") 'efs/show-ielm)
¶True and false
Normally a language would have a “boolean” type for expressing “true” and “false”.
In Emacs Lisp, we have t
and nil
which serve the same purpose.
They are symbols!
(type-of t) ;; symbol (type-of nil) ;; symbol
These symbols are used to provide “yes” and “no” answers for expressions in the language.
There are many “predicates” for the different types which return t
or nil
, we will cover them when talking about each type.
¶Equality
One of the most basic operations you would do on any type is check whether two values are the same!
There are a few ways to do that in Emacs Lisp:
eq
- Are the two parameters the same object?eql
- Are the two parameters same object or same number?equal
- Are the two parameters equivalent?- Type-specific equality predicates
(setq test-val '(1 2 3)) (eq 1 1) ;; t (eq 3.1 3.1) ;; nil (eq "thing" "thing") ;; nil (eq '(1 2 3) '(1 2 3)) ;; nil (eq test-val test-val) ;; t (eql 1 1) ;; t (eql 3.1 3.1) ;; t (eql "thing" "thing") ;; nil (eql '(1 2 3) '(1 2 3)) ;; nil (eql test-val test-val) ;; t (equal 1 1) ;; t (equal 3.1 3.1) ;; t (equal "thing" "thing") ;; t (equal '(1 2 3) '(1 2 3)) ;; t (equal test-val test-val) ;; t
A general rule is that you should use equal
for most equality checks or use a type-specific equality check.
¶Numbers
There are two main types of numbers in Emacs Lisp:
- Integers - Whole numbers
- Floating point numbers - Numbers with a decimal
1 3.14159 -1 -3.14159 1. 1.0 -0
¶Operations
You can perform mathematical operations on these numbers:
(+ 5 5) ;; 10 (- 5 5) ;; 0 (* 5 5) ;; 25 (/ 5 5) ;; 1 ;; Nesting arithmetic! (* (+ 3 2) (- 10 5)) ;; 25 (% 11 5) ;; 1 - integer remainder (mod 11.1 5) ;; 1.099 - float remainder (1+ 5) ;; 6 (1- 5) ;; 4
You can also convert between integers and floats:
truncate
- Rounds float to integer by moving toward zeroround
- Rounds to the nearest integerfloor
- Rounds float to integer by subtractingceiling
- Round up to the next integer
(truncate 1.2) ;; 1 (truncate -1.2) ;; -1 (floor 1.2) ;; 1 (floor -1.2) ;; -2 (ceiling 1.2) ;; 2 (ceiling 1.0) ;; 1 (round 1.5) ;; 2 (round 1.4) ;; 1
See also:
¶Predicates
These predicates will help you identify the number types in code:
(integerp 1) ;; t (integerp 1.1) ;; nil (integerp "one") ;; nil (floatp 1) ;; nil (floatp 1.1) ;; t (floatp "one") ;; nil (numberp 1) ;; t (numberp 1.1) ;; t (numberp "one") ;; nil (zerop 1) ;; nil (zerop 0) ;; t (zerop 0.0) ;; t
¶Comparisons
You can compare two numeric values (even integers against floats):
(= 1 1) ;; t (= 1 1.0) ;; t (= 1 1 1 1 1 1) ;; t (< 1 2) ;; t (> 1 2) ;; nil (> 1 1) ;; nil (> 1.2 1) ;; nil (>= 1 1) ;; t (<= -1 -1.0) ;; t (max 1 5 2 7) ;; 7 (min -1 3 2 4) ;; -1
¶Characters
Characters are really just integers that are interpreted as characters:
?A ;; 65 ?a ;; 97 ?\n ;; 10 ?\t ;; 9 ;; Unicode ?\N{U+E0} ;; 224 ?\u00e0 ;; 224 ?\U000000E0 ;; 224 ?\N{LATIN SMALL LETTER A WITH GRAVE} ;; 224 ;; Control and meta char syntax ?\C-c ;; 3 (kbd "C-c") ;; "^C" ?\M-x ;; 134217848 (kbd "M-x") ;; [134217848]
¶Comparisons
(char-equal ?A ?A) (char-equal ?A 65) (char-equal ?A ?a) case-fold-search (setq case-fold-search nil) (setq case-fold-search t)
¶Sequences
In Emacs Lisp, strings, lists, and arrays are all considered sequences
(sequencep "Sequence?") ;; t (sequencep "") ;; t (sequencep '(1 2 3)) ;; t (sequencep '()) ;; t (sequencep [1 2 3]) ;; t (sequencep []) ;; t (sequencep 22) ;; nil (sequencep ?A) ;; nil ;; What do you expect? (sequencep nil)
You can get the length of any sequence with length
:
(length "Hello!") ;; 6 (length '(1 2 3)) ;; 3 (length [5 4 3 2]) ;; 4 (length nil) ;; 0
You can get an element of any sequence at a zero-based index with elt
:
(elt "Hello!" 1) ;; ?e (elt "Hello!" -1) ;; error -out of range (elt '(3 2 1) 2) ;; 1 (elt '(3 2 1) 3) ;; nil - out of range (elt '(3 2 1) -1) ;; 3 (elt '(3 2 1) -2) ;; 3 (elt '(3 2 1) -6) ;; 3 - seems to always return first element (elt [1 2 3 4] 2) ;; 3 (elt [1 2 3 4] 5) ;; error - out of range (elt [1 2 3 4] -1) ;; error - out of range
¶Strings
Strings are arrays of characters:
"Hello!" "Hello \ System Crafters!" "Hello \\ System Crafters!" (make-string 5 ?!) ;; !!!!! (string ?H ?e ?l ?l ?o ?!) ;; "Hello!"
¶Predicates
(stringp "Test!") ;; t (stringp 1) ;; nil (stringp nil) ;; nil (string-or-null-p "Test") ;; t (string-or-null-p nil) ;; t (char-or-string-p ?A) ;; t (char-or-string-p 65) ;; t (char-or-string-p "A") ;; t (arrayp "Array?") ;; t (sequencep "Sequence?") ;; t (listp "List?") ;; nil
¶Comparisons
You can compare strings for equivalence or for sorting:
string=
orstring-equal
string<
orstring-lessp
string>
orstring-greaterp
(string= "Hello" "Hello") ;; t (string= "HELLO" "Hello") ;; nil (string< "Hello" "Hello") ;; nil (string< "Mello" "Yello") ;; t (string< "Hell" "Hello") ;; t (string> "Hello" "Hello") ;; nil (string> "Mello" "Yello") ;; nil (string> "Hell" "Hello") ;; nil
¶Operations
(substring "Hello!" 0 4) ;; Hell (substring "Hello!" 1) ;; ello! (concat "Hello " "System" " " "Crafters" "!") (concat) (split-string "Hello System Crafters!") (split-string "Hello System Crafters!" "s") (split-string "Hello System Crafters!" "S") (split-string "Hello System Crafters!" "[ !]") (split-string "Hello System Crafters!" "[ !]" t) ;; Default splitting pattern is [ \f\t\n\r\v]+ (setq case-fold-search nil) (setq case-fold-search t)
¶Formatting
You can create a string from existing values using format
:
(format "Hello %d %s!" 100 "System Crafters") (format "Here's a list: %s" '(1 2 3))
There are many more format specifications, mainly for number representations, consult the manual for more info:
¶Writing messages
As you’ve already seen, you can write messages to the echo area (minibuffer) and *Messages*
buffer using the message
function:
(message "This is %d" 5)
It uses the same formatting specifications as format!
¶Lists
The list is possibly the most useful data type in Emacs Lisp.
¶Cons Cells
Lists are built out of something called “cons cells”. They enable you to chain together list elements using the “cons” container.
You can think of a “cons” like a pair or “tuple” with values that can be accessed with car
and cdr
:
car
- Get the first value in the conscdr
- Get the second value in the cons
(cons 1 2) ;; '(1 . 2) '(1 . 2) ;; '(1 . 2) (car '(1 . 2)) ;; 1 (cdr '(1 . 2)) ;; 2 (setq some-cons '(1 . 2)) (setcar some-cons 3) some-cons ;; '(3 . 2) (setcdr some-cons 4) some-cons ;; '(3 . 4)
¶Building lists from cons
There are two ways to build a list from cons cells:
(cons 1 (cons 2 (cons 3 (cons 4 nil)))) (cons 1 '(2 3 4)) (cons '(1 2 3) '(4 5 6)) (append '(1 2 3) 4) (append '(1 2 3) '(4))
¶Predicates
(listp '(1 2 3)) (listp 1) (listp nil) ;; t (cons 1 nil) (append '(1) nil) (listp (cons 1 2)) (listp (cons 1 (cons 2 (cons 3 (cons 4 nil))))) (consp (cons 1 (cons 2 (cons 3 (cons 4 nil)))))
¶Alists
Association lists (or “alists”) are lists containing cons pairs for the purpose of storing named values:
(setq some-alist '((one . 1) (two . 2) (three . 3))) (alist-get 'one some-alist) ;; 1 (alist-get 'two some-alist) ;; 2 (alist-get 'three some-alist) ;; 3 (alist-get 'four some-alist) ;; nil (assq 'one some-alist) ;; '(one . 1) (rassq 1 some-alist) ;; '(one . 1) ;; There is no alist-set! (setf (alist-get 'one some-alist) 5) (alist-get 'one some-alist) ;; 5
¶Plists
A property list (or “plist”) is another way to do key/value pairs with a flat list:
(plist-get '(one 1 two 2) 'one) (plist-get '(one 1 two 2) 'two) (plist-put '(one 1 two 2) 'three 3)
¶Arrays
Arrays are sequences of values that are arranged contiguously in memory. They are much faster to access!
The most obvious form of array is a “vector”, a list with square brackets. Strings are also arrays!
We know how to access elements in arrays, but you can set them with aset
:
(setq some-array [1 2 3 4]) (aset some-array 1 5) some-array (setq some-string "Hello!") (aset some-string 0 ?M) some-string
We can set all values in an array using fillarray
(setq some-array [1 2 3])
(fillarray some-array 6)
some-array
¶Logic Expressions
Logic expressions allow you to combine expressions using logical operators (and
, or
)
You can think of this as operations on the “truthiness” or “falsiness” of expressions!
¶What is true?
When evaluating expressions, everything except the value nil
and the empty list '()
is considered t
!
(if t 'true 'false) ;; true (if 5 'true 'false) ;; true (if "Emacs" 'true 'false) ;; true (if "" 'true 'false) ;; true (if nil 'true 'false) ;; false (if '() 'true 'false) ;; false
¶Logic operators
Emacs provides the following logic operators:
not
- Inverts the truth value of the argumentand
- Returns the last value if all expressions are truthyor
- Returns the first value that is truthy (short-circuits)xor
- Returns the first value that is truthy (doesn’t short-circuit)
(not t) ;; nil (not 3) ;; nil (not nil) ;; t (and t t t t 'foo) ;; 'foo (and t t t 'foo t) ;; 't (and 1 2 3 4 5) ;; 5 (and nil 'something) ;; nil (or nil 'something) ;; 'something (or nil 'something t) ;; 'something (or (- 3 3) (+ 2 0)) ;; 0
¶Conditional expressions
¶The if
expression
As we saw before, the if
expression evaluates an expression and based on the result, picks one of two “branches” to evaluate next.
The “true” branch is a single expression, the “false” branch can be multiple expressions:
(if t 5 ;; You can add an arbitrary number of forms in the "false" branch (message "Doing some extra stuff here") (+ 2 2))
You can use progn
to enable multiple expressions in the “true” branch:
(if t (progn (message "Hey, it's true!") 5) ;; You can add an arbitrary number of forms in the "false" branch (message "Doing some extra stuff here") (+ 2 2))
Since this is an expression, it returns the value of the last form evaluated inside of it:
(if t 5 (message "Doing some extra stuff here") (+ 2 2)) (if nil 5 (message "Doing some extra stuff here") (+ 2 2))
You can use if
expressions inline when setting variables:
(setq tab-width (if (string-equal (format-time-string "%A") "Monday") 3 2))
¶The when
and unless
expressions
These expressions are useful for evaluating forms when a particular condition is true or false:
when
- Evaluate the following forms when the expression evaluates tot
unless
- Evaluate the following forms when the expression evaluates tonil
(when (> 2 1) 'foo) ;; 'foo (unless (> 2 1) 'foo) ;; nil (when (> 1 2) 'foo) ;; nil (unless (> 1 2) 'foo) ;; 'foo
Both of these expressions can contain multiple forms and return the result of the last form:
(when (> 2 1) (message "Hey, it's true!") (- 5 2) (+ 2 2)) ;; 4 (unless (> 1 2) (message "Hey, it's true!") (- 5 2) (+ 2 2)) ;; 4
¶The cond
expression
The cond
expression enables you to concisely list multiple conditions to check with resulting forms to execute:
(setq a 1) (setq a 2) (setq a -1) (cond ((eql a 1) "Equal to 1") ((> a 1) "Greater than 1") (t "Something else!"))
¶The pcase
expression
This one is powerful! We will cover it in a future episode.
¶Loops
There are 4 ways to loop in Emacs Lisp:
¶while
Loops until the condition expression returns false:
(setq my-loop-counter 0) (while (< my-loop-counter 5) (message "I'm looping! %d" my-loop-counter) (setq my-loop-counter (1+ my-loop-counter)))
¶dotimes
(dotimes (count 5) (message "I'm looping more easily! %d" count))
¶dolist
Loops for each item in a list:
(dolist (item '("one" "two" "three" "four" "five")) (message "Item %s" item))
¶Recursion
Can be fun and interesting, but not safe for a loop that will have many cycles:
(defun efs/recursion-test (counter limit) (when (< counter limit) (message "I'm looping via recursion! %d" counter) (efs/recursion-test (1+ counter) limit))) (efs/recursion-test 0 5)
¶What’s next?
- Dive into functions!
- Shorter side videos on
pcase
, regular expressions