He’s the one, who likes all our pretty songs. And he likes to sing along. And he likes to shoot his gun. But he knows not what it means, knows not what it means
This one is actually pretty difficult to write, because I need to tell you how I feel about emacs. But before I start, I need to stress it again: I am a mediocre researcher at best and an unskilled programmer. As I said, I am an emacs poseur. There are many emacs opinion leaders you can listen to and I am not one of those people. The content of this webpage is not an investment advice and does not constitute any offer or solicitation to offer or recommendation of any investment product. Go away and I wish you a merry Christmas. Pfiatdi!
If you don’t go —well— I am going to say something that is not pleasing to your ears.
First thing you might notice from all 23 previous posts is that my emacs lisp sucks. I don’t write in a way that is idiomatic and there must be “correct” slash “better” ways to achieve what I wanted to do. Sure enough, mea culpa. But I also wanted to say that those hacky emacs lisp snippets work for my purposes. I don’t need to write “correct” or “better” emacs lisp. I just need to write some useful emacs lisp that can facilitate what I do. As I said many times, my main job is doing research and writing academic papers; not even writing R code; and absolutely not writing emacs lisp.
I always like the motto “learning enough x to be dangerous”. I have something that I really want to focus on, say developing the next best automated content analytic method or making social sciences adhere better to the modern concept of open science. But writing “better” emacs lisp is not a thing I want to focus on. I just want to know enough emacs lisp to be dangerous. Or put it less flamboyantly, to know enough emacs lisp so that I can use emacs comfortably. Do I need to know how to write generators, macros, and recursive functions in emacs lisp? It would be nice if I am with all of these tricks. But I can do well enough without. Ignorance is not bliss. But ignorance sometimes helps me to focus.
Unlike many people, I know my attention is finite and there are many things want to steal my attention and time. Being able to focus is the only tool for me to excel at the things I want to be good at. I am in my 40s. In my language context, we say: “my one leg is already in coffin”. I am no longer a budding young person who can make infinite number of commitments to learn everything. I need to make compromises. 1
Sure, I need some good tools to do my job but I am certainly not a toolsmith. Is writing emacs lisp fun? Of course it is. And therefore it is quite easy to fall into the trap of writing and rewriting one’s emacs configuration file. For me, investing too much time in toolsmithing is actually procrastination (unless I am a professional toolsmith). Good cooks want to have a good Santoku. But what makes good cooks good cooks is not which Santoku they have, certainly not which Santoku they forged themselves 2. The same way Yukihiro Matsumoto, Donald Knuth, Rich Hickey, Peter Norvig, Jamie Zawinski are (Guido van Rossum was) emacs users, but they are not known for being emacs users. Even Richard Stallman, the author of GNU emacs, is not known for being an emacs user!
I am a scientist. I don’t say a thing is better because I believe in that thing. I am certainly not a fan boy or something like that. I use emacs because I can get my job done with it. The same way a biologist uses an electron microscope or a physicist uses The Large Hadron Collider. Of course, with many other tools I could also get my job done too. VS Code, Neovim, RStudio, JetBrains, you name it. Again, I don’t believe in things; but I evaluate things. From my evaluation, emacs is the most suitable tool. There must be the factor of path dependency here (I have been using emacs for almost 2 decades.) But I know the most important feature of emacs is the freedom it gives me. For example, no vendor of editors would say pressing C-q
repetitively makes any sense. But I have that freedom. Also, not all the tools have the freedom for me to create a minimal interface. emacs provides me that freedom too.
I rarely address to you, my dearest readers. But if you really need my advice on “learning” emacs, my advice is to hack your emacs NOW with the “self-documentation” as your guide —Like I did in the previous 23 posts—, instead of pursuing the hypothetical adventure of “learning emacs (lisp)”. From my experience as an educator, learning a programming language for the sake of learning a programming language destines to futile 3. You won’t need to “learn emacs lisp” to start hacking emacs. You learn enough emacs (lisp) by hacking emacs. No one knows your need. The emacs configuration you hack together may not be “better” in someone else’s opinion, but that’s what suits you.
Before I go, I will talk about the background information for one last time. Mostly about the emacs lisp constructs which I have used but had no chance to explain.
Lists are often used in customizing emacs. Unlike many lisp books to talk about con cells, car
, cdr
, recursion or things like that, I just want to tell you two things about lists.
There are actually three types of lists in emacs lisp. But I just want to talk about regular list and alist. A regular list is what one would expect like the list in Python or array in Javascript.
(setq languages '("python" "r" "javascript" "elisp" "C++"))
(nth 2 languages) ;; javascript
Association list (alist) is a list but more like dict (hash table) in Python. It can record key-value pairs
(setq creators '((Python . "Guido van Rossum")
(C++ . "Bjarne Stroustrup")
(Perl . "Larry Wall")
(Ruby . "Yukihiro Matsumoto")))
(assoc 'Ruby creators) ;; give a key-value pair
(cdr (assoc 'Ruby creators)) ;; just "Yukihiro Matsumoto"
alist is being used a lot in configuration. This is an example:
(use-package atomic-chrome
:config
(atomic-chrome-start-server)
(setq atomic-chrome-buffer-open-style 'full)
(setq atomic-chrome-url-major-mode-alist
'(("github\\.com" . poly-markdown+r-mode)
("overleaf\\.com" . latex-mode)))
)
The :bind
syntax of use-package
also uses an alist.
(use-package ess
:bind (
:map ess-r-mode-map
("_" . 'ess-insert-assign)
("C-q" . 'ess-eval-region-or-line-and-step)
("C-c C-k" . 'ess-request-a-process)
:map inferior-ess-r-mode-map
("_" . 'ess-insert-assign))
)
I want to make sure you know the difference between setq
and let
, although you probably know it already. Both of them create variable bindings, whereas setq
can also be used to mutate the value of a variable.
First, it is possible to make multiple bindings with one single setq
.
(setq x 123 y 100)
(- x y) ;; 23
(setq x 100)
(- x y) ;; 0
let
creates some local bindings that are valid only within the body (the local scope; or the block scope, if you prefer the Javascript term). Outside of the body (the closing paren of let), those bindings are not available.
(let ((pi-approx (/ 22 7)))
(- pi-approx pi)) ;; -0.14
pi-approx ;; error
There are several ways to do conditional jump. I’ve heard that true lispers use only cond
. But I don’t buy it.
if
is usually used when one wants to have “if - then - else”. A clause is one S-expression.
(let ((age 41))
(if (< age 40) ;; predicate
(message "You are young") ;; then clause
(message "You are old"))) ;; else clause
when
is used when one doesn’t want to have the else clause. The following returns nil
.
(let ((age 41))
(when (< age 40)
(message "You are young")))
unless
is the reverse of when
.
(let ((age 41))
(unless (>= age 40)
(message "You are young")))
True lispers may only use cond
. But there are reasons why the above syntactic sugars (if
, when
, unless
) of cond
exist.
(let ((age 41))
(cond ((< age 40) (message "You are young"))
(t (message "You are old"))))
In other programming languages, blocks are used to run multiple lines of code conditionally. For example, the curly braces in the following Rust program create blocks.
let age = 41;
let compensation =
if age >= 40 {
println!("You are old. I'll give you more compensation.");
age * 20.0
} else {
println!("You are young. You don't need that much compensation.");
age * 0.5
};
println!("You receive {}€ in compensation.", compensation);
if
in emacs lisp takes just one S-expression (sexp) as a clause, not a block. What is one sexp? This is one sexp: (message "hello")
. This is also one sexp: (message "%f" (/ 22.0 7))
. But here are two sexps: (setq approx-pi (/ 22.0 7)) (message "%f" approx-pi)
. If you enter it in ielm
, the output is an error:
progn
is used to create the equivalent of a block by combining multiple sexps into one. There are actually several of this kind of progn
functions (progn
, progn1
, progn2
…). Let’s focus on progn
. This runs fine in ielm
.
(progn (setq approx-pi (/ 22.0 7)) (message "%f" approx-pi))
progn
runs all S-expressions but only return the last value.
(progn
(/ 1.0 2 )
(/ 9 3)
(/ 22.0 7))
The above rust program can be expressed in emacs lisp as 4:
(let* ((age 41)
(compensation
(if (>= age 40)
(progn
(message "You are old. I'll give you more compensation.")
(* age 20.0))
(progn
(message "You are young. You don't need that much compensation.")
(* age 0.5))
)))
(message "You receive %f€ in compensation." compensation))
On a historical note: History tells us that the victims of political attacks are often devotees who make compromises, not the people in the political extremes. It is because people who make compromises are perceived as disloyal and can be easily sold. In reality, many people —even extremists— make compromises in private and of course also, in silence. So the real targets of political attacks are usually people who voice out their compromises in public. In an opinion climate like this, people tend to express a more extreme form of their political opinion in public, or remain silence (See Elisabeth Noelle-Neumann’s spiral of silence). Therefore, it’s unwise, like me, to make my compromises public like this. Also, it won’t score me points among the potential readers of this piece. ↩
I also know that the community isn’t sustainable if all of us think like me. So, I should learn enough toolsmithing to make something. But you don’t need to know a lot to do that. ↩
For example, I can tell from the outset that if a person says “I am learning R”, this person has a 80% chance of not using R anymore. However, if a person says “I am trying to do data analysis with R”, the chance is probably 10%. The same way goes with “I am learning C++” and “I am trying to optimize my existing R code by rewriting some parts of it in C++”. ↩
I don’t have a chance to explain let*
. let
does all bindings at the same time, whereas let*
does bindings one by one. In this case let*
is needed because I need to use age
in the letting of compensation
. Also, don’t argue with me about the mutability status of the two variables age
and compensation
is not the same as the Rust program. I am not good at proving the equivalency of two programs. ↩