Learning a little elisp
There’s tons of detailed information about Emacs LISP — aka Emacs Lisp, elisp, ELisp, and “oh my god they love parentheses” — out there. I just want my old “Babysteps” approach, so all the detailed sites won’t be so intimidating.
Gotta do it myself, I guess.
Why
So far I have treated elisp as an arcane configuration language. But it’s so much more than that. It’s also an arcane programming language. I do love learning programming languages.
I’ll have an easier time configuring Emacs, and most likely get strange new ideas for ways to extend my frenemy text editing environment.
How
Using Emacs, of course! A little bit with the deep integration for both evaluation and documentation_ of Lisp. Probably a bit more with Org Babel, which provides a layer for evaluating code and exporting the results — say, for example, to a blog post like this one.
Expect side notes about Doom Emacs, since that’s the flavor I use lately.
Let’s get started
I looked up “Hello World in ELisp” and found something like this.
(message "Hey World!")
ELisp evaluation
Want to write some Emacs Lisp? Here you go.
- open Emacs
- type
(message "Hey World!")
- put your cursor — the point — just outside the closing parenthesis.
- Hit [[C-x e]]
- Emacs prints
Hey World!
Boom. Done.
The ()
indicate an s-expression. That’s a symbolic expression, or
sexpr if you’re cool. S-expressions aren’t quite the atoms of a Lisp
program. There are smaller bits, like the symbol message
or the value "Hey World!"
. But it’s the smallest useful element. Oh I know. S-expressions
are the molecules of a Lisp program.
No? How about words vs sentences? Okay, whatever.
This particular s-expression holds an ordered pair, message
and "Hey World!"
. Pair because there are two items. Ordered because the order
matters.
When ELisp sees an ordered pair, it knows what to do:
- figure out what it gets from the second thing
- hand that to the first thing
- hand that result to you
The part that feels magic is each of the items in the pair can be s-expressions
too! Try (sqrt (* 37 37))
. 37.0
, right?
That *
is for multiplication. So we’re multiplying 37
by 37
and
proving to ourselves that sqrt
hands us back 37
. It’s a bit of a
pointless example, but hey welcome to me learning stuff. And there’s my first
lesson:
A Lisp program is pretty much just infinitely nested s-expressions.
And macros. Macros, near as I can tell, are infinitely nested s-expressions with gloves and a nice hat.
BTW I don’t know Lisp. I hope you did not come here expecting a tutorial.
ELisp documentation
When we have a question about ELisp functions, we don’t need to look everything up online. Emacs comes with notes.
- put point over
message
in(message "Hey world!")
- hit [[C-h f]]
- see the prompt asking me to specify a function, with
message
pre-filled - hit [[ENTER]]
- Learn things!
ELisp in Org Babel
This is great and all, but I am less concerned about live evaluation of ELisp. Org mode is more interesting to me. I could make my config smarter. For example, only tangle a section if it’s relevant for that machine.
And, of course, really handy for blogging about ELisp.
I need a code block written in a language that Babel knows. It should not surprise us that Babel knows ELisp.
#+begin_src elisp
(message "Hey World!")
#+end_src
I press C-c C-c with point over the code block.
Suddenly: a #+RESULTS:
block!
#+RESULTS:
: Hey World!
I can also write my ELisp inline:
src_elisp{(sqrt (* 37 37))}, right?
Written like this, Babel replaces my code with its result when ox-hugo
exports the post.
All right. That’s the very basics of evaluating ELisp in Emacs generally and Org mode in particular.
Let’s get back to the code, please. How do I do variables?
Displaying a variable
Let’s see. setq
to set a variable for my name. identifiers can be pretty
much whatever. I’ll use lowercase letters and a hyphen.
Looks like format
can smush it into a string for message
.
(setq my-name "Brian")
(message
(format "hello %s" my-name))
format
does its work and hands the result back to message
, which displays
the result.
hello Brian
Thing is, now my-name
is floating around forever what with being a global
variable.
(message my-name)
Brian
What if I used a local variable instead?
(let ((new-name "Whozzomongo"))
(message new-name))
Whozzomongo
But back out here it doesn’t exist.
(new-name)
You don’t see anything out here, but when I tried to C-c C-c that, Emacs complained:
Symbol’s function definition is void: new-name
I consider that a good thing. Global variables make me nervous, especially in long-running applications.
So I know how to set global or local variables. I know how to display them.
How to get them from the user?
Getting user input
Xah Lee gives a nice rundown on how to get user input.
read-string
is the one I want.
(read-string "What's your name? ")
read-string
returns whatever I answer.
Waffle Smasher The Magnificent Pineapple
Let’s make a question prompt. The inside-out approach of nested evaluation confuses me a bit, so I’ll happily let Emacs indent things however it wants.
(message
(let
((question "What's your name?")
(message "Go to bed, %s!"))
(format message
(read-string (format "%s " question)))))
Go to bed, Dude!
I did some things. read-string
puts the cursor right after the
question
prompt. So to help myself while I’m figuring all this out, I
created some local variables. question
holds the question to be answered.
message
holds the —
Wait, there’s already a global standard function called message
!
It’s cool. By the time I need the function, let
is done and my variable
doesn’t exist. Still. I shouldn’t make this a habit.
Wrapping it in a function
I wasn’t planning on looking at functions today, but I’m more than halfway there already.
(defun ask-and-respond (question-for-user our-response)
"Ask the user a question and show them a response."
(interactive)
(message
(format our-response
(read-string (format "%s " question-for-user)))))
Use the defun macro to define functions. It’s similar enough to function definitions in other languages.
(defun NAME (ARGUMENTS…)
"A docstring"
THE CODE)
Though there are some differences right off the bat. ask-and-respond
needs
user input. ELisp requires I mark those as interactive_.
Other than that it’s similar enough to function definitions in other languages.
I already know how to call a function.
(ask-and-respond "What's your name?" "Goodnight, %s!")
Goodnight, Brian!
Okay, time to take my own hint. Good night!