Skip to content

Instantly share code, notes, and snippets.

@Gavinok
Created August 14, 2022 16:16
Show Gist options
  • Save Gavinok/e68a21b0e72607b20a1e0255b3aeb9fe to your computer and use it in GitHub Desktop.
Save Gavinok/e68a21b0e72607b20a1e0255b3aeb9fe to your computer and use it in GitHub Desktop.
Learning Emacs Lisp (Functions, Lists, and Homoiconicity

POSTED Emacs Lisp Everything You NEED To Know

Intro

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.

Quick overview of Emacs

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

Now onto learning Emacs Lisp

Disclaimer

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 basics

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)

Defining a function

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.

Calling a function

;; funcname(10)
(funcname 10)
;; 10 + 1
(+ 10 1)

Lists

;; [ 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

Now onto the confusing part

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))

Quote

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))

Conclusion

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment