Emacs is one of man’s greatest achievements being the most extensible “text editor” out there.
Emacs has an active community of tools to extend it,
- a light weight graphical and terminal interface
- The best note taking and organization tool out there
- a powerful configuration language.
Now that last part is probably the biggest stopping point for people.
Most people see example code and immediately turn away thinking it’s cryptic, hard to read/write, etc
I’m here to explain both why elisp is both simple, powerful and while it’s different from most other languages that is actually it’s greatest advantage.
Before we get into emacs lisp here is a quick tour for those of you who are new to emacs.
I assume some of you are already somewhat familiar with emacs lisp so if this doesn’t interest you feel free to skip to the next Timecode.
Open emacs C-x C-e
(message "hello")
Maybe just tell people to use ielm for now
Explain ~/.emacs.d/init.el
is where you can store your config
You can look up variables with C-h v
and functions with C-h f
The priority here is going to be explaining the confusing parts of lisp/elisp for new users. So this isn’t going to be complete just as much as you need in order to start reading and writing elisp. This may be a little overwhelming for those with zero programming experience but just playing with emacs will likely fill in the gaps.
NOTE: I WILL be making lots of generalizations to make this easier to follow I know there are plenty of details I am overlooking but in reality its a necessary evil. 2 big points are macros and scoping which are important but to keep the video from getting to long I omitted them.
For reference I will be comparing it to python but it should be pretty easy to follow if you don’t have experience with emacs
The nice thing about lisp is that basically everything is a funciton
call so the syntax is pretty easy to understand though it takes a
little getting used to.
First thing is defining a variable
Don’t @ me I know there are other ways to define a variable
;; varname = 10
(setq varname 10)
we use defun
to define a function
def funcname (arg)
print(10)
(defun funcname (arg)
(print arg))
If you wanted to make that function available in the M-x interface
just add (interactive)
to the function
(defun funcname (&optional arg)
(interactive)
(print 10))
Note just how similar the 2 are.
If you trust the indentation and ignore the parentheses you start to see why parentheses are not actually an issue. Instead it’s considered a strength of the language.
;; funcname(10)
(funcname 10)
;; 10 + 1
(+ 10 1)
;; [ 1, 2, 3, 4 ]
'(1 2 3 4)
Lists are the most common data structure used in lisp
It’s actually where it gets it’s name from
To get the first element of a list we can use the function car
(car '(1 2 3))
to get the rest we use cdr
Why CDR
there is a history behind the name but for now you can keep it
together by remembering that a
comes before d
.
(cdr '(1 2 3))
Now what if I told you that a list can contain basically anything
for example
'(+ 1 2)
While that may look like a function call it’s actually a list
(car '(+ 1 2))
The +
in this case is called a symbol. A symbol is basically a element
that has not been evaluated.
The reason this is so strange is actually that '
at the start of the list.
The quote is basically saying don’t evaluate this. so instead of returning an expression to evaluate it’s getting a list of symbols.
If we wanted to get the symbol + we could also use that quote
+
Now for those of you wondering how do you create a normal list where
all the arguments are evaluated you actually use they (list)
function
(setq name 10)
(list name 1 2 3)
If we did this using quote we would instead get
(setq name 10)
'(name 1 2 3)
Hopefully that clears things up.
Why not explain list
at the start? you may be wondering
Well that wouldn’t be as much of a plot twist now would it.
Now I want to peel back a bit of abstraction just to help you better understand what symbols are and how they are used in emacs
When we define a variable we are actually “binding” a value to that symbol
(setq name 10)
is the same thing as
(set 'name 9)
now when we evaluate
name
all emacs is going to do is look up that symbol and find it’s value It’s just that simple
Now just before we get too far from the topic I won’t go too far into
it but I wanted to mention quasiquoting
since chances are you will run
into it
If the list section already was a little much maybe skip to the next part and come back to this later.
Basically quasiquoting
allows you to create a quoted list with some
symbol/expression also evaluated
(setq name "Gavin")
(setq age 10)
`(1 2 ,name age)
Note that the comma (commonly refereed to as unquote) indicated that the next expression/symbol should be evaluated.
Think of it as a quote that can selectively be disabled.
finally I also wanted to mention cons cells. all they really are is a pair of elements in a list
(cons 1 2)
put the pairs together and terminating them with nil
gives you a list
(cons 1 (cons 2 nil))
'nil
Ok onto the next point of confusion.
So since we setq
binds symbols to a value why not just use setq
for functions?
well lets try
(setq v (lambda () (insert 10)))
lambda is basically a way to define an unnamed function.
Now what happens when we call it?
(v)
We get the error
> progn: Symbol’s function definition is void: a
Now whats going on?
Often one of the most confusing elements of learning emacs lisp is understanding that functions and variables use different namespaces.
What exactly does that mean though?
lets look at another example
(setq a 10)
(defun a ()
"hello world")
So here we have define a function and a variable with the same name.
What do you think this a will evaluate to?
a
Ok so it’s using it as a variable. What about this then?
(a)
Yep it called the function version! so what exactly is happening?
Well the answer is that the two symbols are actually in seperate namespaces.
Now how could we access these different namespaces outside of a trivial function call?
Well we can access the function namespace using the #'
prefix
(setq a (lambda () 10))
(funcall a)
Now that’s just giving us a
what’s going on here?
Well the a
we are getting is actually the a
from the function namespace
Now how can we use this? Lets use the simple example funcall
all it
does is call the first argument as a function.
Why is this significant? Well it will call the argument as a function not a function it’s self.
let’s use it with our previous example of using a lambda with setq
(setq hello (lambda () "hello world"))
(funcall hello)
Now would you look at that! it worked!
Now if we wanted to call a function when we would have to five funcall a function using the #'
prefix
(defun test () "testing")
(funcall #'test)
It’s as simple as that. Now if you guys wanted a more useful scenario we can use mapcar
mapcar
takes a function and applies it to each element in a list
(1+ 1)
(defun q (x) (+ 1 x))
(mapcar q '(1 2 3))
Yep that’s right is a 1+
function
We could also do the same with a lambda and a variable
(mapcar (lambda (x) (+ 1 x)) '(1 2 3))
(setq x (lambda (x) (+ 1 x)))
(mapcar x '(1 2 3))
While I would have liked to cover more I figured I should hold off and possibly cover it in a future video
If that interests you be sure to leave a like or let me know down in the comments