(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 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'")