(Re)generate: (find-elisp-intro)
Source code: (find-efunction 'find-elisp-intro)
More intros: (find-eev-quick-intro)
(find-emacs-keys-intro)
(find-eev-intro)
This buffer is _temporary_ and _editable_.
It is meant as both a tutorial and a sandbox.
The quickest way to open or recreate this is with `M-7 M-j'.
This intro is a very quick introduction to Emacs Lisp. Its intent
is not to teach people how to _write_ Elisp code, only to teach
them how to _read_ Elisp code. Its prerequisites are just these
two sections of the main tutorial:
(find-eev-quick-intro "2. Evaluating Lisp")
(find-eev-quick-intro "3. Elisp hyperlinks")
Different people prefer different kinds of tutorials.
Many people love the eintr, but I don't: (find-node "(eintr)Top")
This tutorial here is what I would have liked to have had access to
when I started learning Emacs Lisp.
The ideas behind the style of this tutorial are explained here:
http://anggtwu.net/find-elisp-intro.html
(find-1stclassvideo-links "2022findelispintro")
TODO: integrate this with these older intros:
(find-eval-intro)
(find-defun-intro)
1. Introduction
The main tutorial for eev starts by explaining the syntax of Emacs
Lisp - "Elisp" from here onwards - using a simple example. To evaluate
this sexp
(+ (* 2 3) (* 4 5))
Emacs evaluates first its subexpressions, (* 2 3) and (* 4 5), and
then it calls `+' with the results - i.e., it runs (+ 6 20). Try:
2
3
(* 2 3)
4
5
(* 4 5)
(+ (* 2 3) (* 4 5))
(list (* 2 3) (* 4 5))
In the last sexp the function `list' receives two numbers, 6 and 20, but
returns a list, (6 20), not a number - and this is an introduction to
the idea that Elisp functions can receive all kinds of Elisp objects
and return all kinds of Elisp objects.
Elisp objects include:
numbers, like 0, 4, -42, and 2.5,
strings, like "foo" and "bar",
symbols, like `*', `+' and `list', and
lists, like (6 20), (* 2 3), and (+ (* 2 3) (* 4 5)).
The ones listed above are the only ones that we will treat in this
introduction. For more on the other types of lisp objects, see:
(find-elnode "Lisp Data Types")
(find-elnode "Programming Types")
(find-elnode "Editing Types")
2. Lists and conses
Lists are implemented using "cons"es. For details and lots of great
diagrams see these two articles in the Wikipedia:
https://en.wikipedia.org/wiki/Cons
https://en.wikipedia.org/wiki/Cons#Lists
https://en.wikipedia.org/wiki/Cons#Trees
https://en.wikipedia.org/wiki/S-expression
The last one explains in detail what are "sexp"s.
The function `cons' "constructs" a bigger list by prepending an
element to it:
(list 22 33)
(cons 11 (list 22 33))
(list 11 22 33)
The functions `car' and `cdr' both receive a list and return the
components of its first cons. The `car' of a list is its first
element, and the `cdr' of the list is the "rest" of the list. Try:
(list 11 22 33)
(cons 11 (list 22 33))
(car (cons 11 (list 22 33)))
(cdr (cons 11 (list 22 33)))
(car (list 11 22 33))
(cdr (list 11 22 33))
(cons (car (list 11 22 33)) (cdr (list 11 22 33)))
3. `quote'
Not all functions evaluate their arguments. For example, `quote'
expects a single argument and returns it unchanged, without evaluating
it. Try:
(* 2 3)
(+ (* 2 3) (* 4 5))
(quote (* 2 3))
(quote (+ (* 2 3) (* 4 5)))
Elisp has an abbreviation for `quote' - a single "'". Try:
(quote (* 2 3))
'(* 2 3)
(quote '(* 2 3))
(quote (quote (* 2 3)))
Elisp has several other functions besides `quote' that do not evaluate
their arguments. These functions are called "special forms" and we
will see more about them in section 5, 6, 8, and 9.
(find-elnode "Special Forms")
4. Symbols
Note that symbols and strings are different:
(quote *)
'*
"*"
(symbol-name (quote *))
(symbol-name '*)
Numbers and strings "evaluate to themselves". Try:
22
"22"
(+ 22 33)
(concat "22" "33")
If you need more evidence that 22 and "22" are different run the sexps
below - they give errors! Numbers can be added but not concatened, and
strings can be concatenated but not added.
(+ "22" "33")
(concat 22 33)
5. Variables
When we evaluate a list like (* 2 3) the symbol in the beginning of
the list, `*', is interpreted as the name of a function... but when we
evaluate a symbol by itself it is interpreted as the name of a
variable, and it returns the value of that variable. Try:
(set 'a 2)
a
(* 10 a)
(set 'a 3)
a
(* 10 a)
We will often write examples like the one above more compactly, like
this:
(set 'a 2)
(set 'a 3)
a
(* 10 a)
If you execute lines 1, 3, and 4 in it you get one behavior, and
if you execute lines 2, 3, and 4 you get the other one. There are
some exercises on "choosing the right order" here:
(find-eval-intro "3. What to execute, and in what order")
Again: note that evaluating the symbol `a' returns the value of a "as
a variable".
The function `setq' works like `set' but it doesn't expect us to quote
explicitly its first argument. Try:
(set 'a 2)
(setq a 2)
(set 'a (* 2 3))
(setq a (* 2 3))
a
(* 10 a)
`setq' is a special form - it doesn't evaluate its first argument but
evaluates the second one. See:
(find-elnode "Setting Variables" "Special Form: setq")
6. Defining functions
Each symbol has a value "as a variable" and a value "as a function",
and these two values are independent from one another. We can define
functions with `defun'. Try:
(defun foo (a) (* 10 a))
(defun foo (a) (* 100 a))
(foo 2)
(foo 3)
(setq foo 2)
(setq foo 3)
(foo foo)
foo
(symbol-value 'foo)
(symbol-function 'foo)
Function definitions are stored as lists starting with the symbol
`lambda' ("lambda expressions"). If you are a beginner you should
treat the details of this as a curiosity - but try the example below:
(defun foo (a) (* 10 a))
(defun foo (a) (* 100 a))
(fset 'foo (lambda (a) (* 10 a)))
(fset 'foo (lambda (a) (* 100 a)))
(symbol-function 'foo)
(foo foo)
For more information, see:
(find-elnode "Lambda Expressions")
(find-elnode "Defining Functions" "defun name args")
(find-elnode "Function Names")
(find-elnode "Symbol Components")
(find-elnode "Function Cells")
(find-elnode "Function Cells" "symbol-function symbol")
(find-elnode "Function Cells" "fset symbol definition")
Not all functions are represented as lambda expressions. The functions
of Emacs that are implemented in C are represented as "subr"s, that
are one of the kinds of objects that I said that I wouldn't discuss in
this introduction. See:
(symbol-function '+)
(find-elnode "Programming Types" "Primitive Function Type")
(find-elnode "Primitive Function Type" "subrs")
Also, functions can be byte-compiled (see section 11), and they
can expect and receive any number of arguments. See:
(find-elnode "Defining Functions" "defun bar (a &optional b &rest c)")
Try:
(defun bar (a b &optional c d &rest e) (list a b c d e))
(bar 1 2 3 4 5 6)
(bar 1 2 3 4 5)
(bar 1 2 3 4)
(bar 1 2 3)
(bar 1 2)
(bar 1)
7. `read' and `eval'
We've been evaluating all sexps with `M-e'... but try:
'(* 2 3)
(eval '(* 2 3))
(setq a '(* 2 3))
(setq a '(* 4 5))
a
(eval a)
"(* 2 3)"
(read "(* 2 3)")
(eval (read "(* 2 3)"))
It turns out the `M-e' first extracts a sexp from the current buffer
as a string, then `read' converts that string to a Lisp object -
usually a list or symbol -, and `eval' evaluates that list or symbol.
There is a also a function `prin1-to-string' that does the opposite of
`read', as in the diagram below:
read
"(* 2 3)" -----------------> (* 2 3)
<----------------- |
prin1-to-string |
|
v
6
Try:
(* 2 3)
'(* 2 3)
(prin1-to-string '(* 2 3))
So: programs and functions can be represented as lists, and also as
strings that be converted into lists by `read'; they can be inspected,
edited and modified on-the-fly; and we can write Lisp code that
generates and evaluates more Lisp code. Eev does this in a handful of
situations - for example:
(find-eev-quick-intro "find-code-c-d")
(find-here-links-intro "4. `find-here-links-3'")
(find-eev-quick-intro "4.2. `find-ekey-links' and friends")
The idea that programs are lists is far more powerful than it
looks at first sight. See, for example:
https://sep.yimg.com/ty/cdn/paulgraham/bbnexcerpts.txt
http://www.paulgraham.com/diff.html
http://www.paulgraham.com/lisp.html
(find-elnode "Simple Macro")
Some functions in eev expect code that is meant to be `eval'-ed
later. For example:
(find-multiwindow-intro "2. `find-wset'")
(find-multiwindow-intro "2. `find-wset'" "(find-wset" "13o_2o_o")
(find-wset "13o_2o_o" '(find-ebuffer "B") '(find-ebuffer "C"))
The functions in the `code-c-d' family produce code that is first
`read' and then `eval'-ed. See:
(find-eev-quick-intro "9.1. `code-c-d'")
(find-code-c-d "CODE" "/DIR/" :info "INFO")
8. `let' and `let*'
See:
(find-elnode "Local Variables" "Special Form: let ")
(find-elnode "Local Variables" "Special Form: let* ")
Try:
(setq y 2)
y
(let ((y 1)) y)
(let ( y ) y)
(let ((y 1) (z y)) (list y z))
(let* ((y 1) (z y)) (list y z))
9. More on strings
Strings in Elisp can span several lines and can contain many
kinds of backslashed "escape sequences", like "\n". Elisp
also has a syntax for characters, that starts with a "?" and
supports the same escape sequences as strings. See:
(find-elnode "Basic Char Syntax")
(find-elnode "Character Type")
Remember - from
(find-eev-quick-intro "2. Evaluating Lisp" "`M-0 M-e'")
that `M-0 M-e' and `M-0 M-E' highlight the sexp before point...
you can use that to check the extent of a string whose
backslashes are hard to interpret visually. For example:
"\
\\\"\\\\"
10. Backquote
The backquote, "`", works like the quote, "'", but when a
backquoted list is evaluated it is processed recursively and its
components preceded by "," or ",@" are evaluated and replaced
by their values. The details are quite tricky - try these
examples:
`(foo ,(+ 2 3) bar)
`(foo ,'(+ 2 3) bar)
`(foo ,(list 2 3) bar)
`(foo ,@(list 2 3) bar)
and see:
(find-elnode "Backquote")
The functions in eev that use `find-elinks' typically use
backquotes a lot. See:
(find-links-conv-intro "3. Classification")
(find-eev "eev-elinks.el" "find-elinks")
(find-eev "eev-elinks.el" "find-efunction-links")
They are the hardest ones to read in the eev source.
11. Byte-compiled functions
Most functions in Emacs are byte-compiled - which means that
their function cells contain a "byte-code" instead of a lambda
expression. These byte-codes are very hard for humans to read.
See:
(find-elnode "What Is a Function" "byte-code function")
(find-elnode "Byte-Code Type")
(find-elnode "Byte Compilation")
(find-elnode "Disassembly")
Here is an example:
(find-efunctiondescr 'find-file)
(find-efunction 'find-file)
(symbol-function 'find-file)
(find-efunctionpp 'find-file)
(find-efunctiond 'find-file)
The `find-efunctionpp' link above takes the content of the
function cell of `find-file' and "pretty-prints" it, i.e.,
indents it in a nice way, but the result in this case is
unreadable... and the `find-efunctiond' link shows a decompiled
version of that byte-code, which is only slightly better. Both
the `find-efunctionpp' and the `find-efunctiond' links show
internal representations that are very different from the source
code. Compare that with a case in which the function is not
byte-compiled:
(find-efunctiondescr 'find-fline)
(find-efunction 'find-fline)
(symbol-function 'find-fline)
(find-efunctionpp 'find-fline)
The `(find-efunctionpp 'find-fline)' shows a lambda expression
that is very similar to the defun that defined `find-fline'.
11.1. Why eev avoids byte-compilation
All the source files of eev have a "no-byte-compile: t" in
them. See:
(find-eev-install-intro "7.1. Byte-compilation")
(find-eevgrep "grep --color -nH -e no-byte-compile: *.el")
(find-elnode "Byte Compilation" "no-byte-compile: t")
(find-enode "Specifying File Variables")
This `no-byte-compile: t' is non-standard, but it is a deliberate
design choice. I tried to make eev as beginner-friendly as
possible, and beginners find byte-compiled functions confusing,
so I avoided them as much as possible. Remember that several
functions in eev define other functions - for example:
(find-eev-quick-intro "9.1. `code-c-d'")
(find-eev-quick-intro "9.1. `code-c-d'" "mass-produced")
(find-eev-quick-intro "9.1. `code-c-d'" "find-code-c-d")
and if you try to understand what a hyperlink function like one
below does by typing `M-h M-f' on it,
(find-efile "subr.el")
then the `find-efunction' link in the `M-h M-f' buffer will not
work - but the `find-efunctionpp' link will. Try:
(find-efunction 'find-efile)
(find-efunctionpp 'find-efile)
11.2. How `find-efunction' works
Eev defines hyperlink functions called `find-efunction',
`find-evariable' and `find-eface' that are wrappers around the
standard Emacs functions `find-function', `find-variable' and
`find-face-definition'; the eev variants support pos-spec-lists.
Try:
(find-efunction 'find-fline)
(find-function 'find-fline)
(find-evariable 'ee-hyperlink-prefix)
(find-variable 'ee-hyperlink-prefix)
(find-eface 'eepitch-star-face)
(find-face-definition 'eepitch-star-face)
The Emacs functions are defined here:
(find-efile "emacs-lisp/find-func.el")
and their inner workings are quite complex. To begin with, hey
use `symbol-file', that works on the variable `load-history'.
Here are some links to documentation and tests:
(find-efunctiondescr 'symbol-file)
(find-elnode "Where Defined")
(symbol-file 'find-fline 'defun)
(symbol-file 'find-efile 'defun)
(symbol-file 'ee-hyperlink-prefix 'defvar)
(symbol-file 'eepitch-star-face 'defface)
(find-eloadhistory "eepitch")
(find-eloadhistory "eepitch" "eepitch-star-face")
The functions in "find-func.el" use `symbol-file' to find the
file where a given symbol was defined, and then search for a
defun, defvar of defface in it that _looks like_ the definition
that we are looking for.
Emacs knows that `find-efile' was defined in "eev-code.el",
because of:
(symbol-file 'find-efile 'defun)
(find-eloadhistory "eev-code" "find-efile")
but if we run
(find-function 'find-efile)
this will fail, because Emacs will look for something like
"(defun find-efile " in "eev-code.el", and it will not find
it; it doesn't know that `find-efile' was defined by this
`code-c-d':
(find-eev "eev-code.el" "code-c-d-s")
(find-eev "eev-code.el" "code-c-d-s" "\"e\"")
Let's be even more precise. "find-func.el" defines these
low-level functions,
(find-efunctiondescr 'find-function-noselect)
(find-efunctiondescr 'find-variable-noselect)
(find-efunctiondescr 'find-definition-noselect)
that return structures of the form (BUFFER . POS), and eev
defines a function
(find-efunctiondescr 'find-ebufferandpos)
(find-efunction 'find-ebufferandpos)
that jumps to a (BUFFER . POS); `find-efunction' and friends are
implemented using `find-ebufferandpos'. Try:
(find-ebufferandpos
(find-function-noselect 'find-fline)
)
(find-ebufferandpos
(find-variable-noselect 'ee-hyperlink-prefix)
)
(find-ebufferandpos
(find-definition-noselect 'eepitch-star-face 'defface)
)
These `find-*-noselect' functions work quite well but are not
100% reliable - for example, if an elisp file has several
definitions for the same function, variable, or face, the
`find-*-noselect's don't know which ones were executed, neither
which one was executed last, overriding the other ones... and it
may return the position of a defun, defvar, or defface that is
not the "active" one. In eev redefinitions like these ones
(code-pdf-page "foomanual" "/usr/src/foo-1.2.3/manual.pdf")
(code-pdf-page "foomanual" "/usr/src/foo-1.2.4/manual.pdf")
are quite common, and
(find-efunctionpp 'find-foomanualpage)
will give you information about the current definition.
12. Some advanced topics
See: (find-lexical-intro)
(find-kla-intro "8. `cl-loop'")
(find-kla-intro "9. `cl-defun'")