Table of Contents
This article is part of the series Living with Emacs Lisp. This article is a bit long, and beginners probably don’t need to read the sections after the
- - - part.
Alice is a C programmer and a Lisp newbie. One day, Alice is writing a Lisp code and comes across a problem. A problem that she would use double pointers for if she were writing in C. Lisp doesn’t seem to have double pointers. She is stuck. A good problem solver when stuck would temporarily give up on the problem, and come up with a simpler problem and try to solve that first. Alice, being a good problem solver, comes up with the simplest problem, which is:
“I cannot write a Lisp function that takes a list and adds an element to its front. What now?”
Indeed, what now? There are many ways to work around this, but before we go, let me introduce a second problem, a bit more involved than the first problem:
“I cannot write a Lisp function that takes a list and adds an element to its front if the element is not in the list. What now?”
and a third problem:
“I cannot write a Lisp function that removes an element from the front of the list. What now?”
All examples in this article are for Emacs Lisp. Let’s see workarounds.
1. write a function that returns the result instead
You can’t write a function that does the job, but you can still write a function that returns the result you want, which you can set back to the variable. For the three problems I mentioned, you don’t have to write such functions because Emacs Lisp already provide them:
(setq numbers (list 10 20 30)) (setq numbers (cons 0 numbers)) (print numbers) ;; ⇒ (0 10 20 30) (setq numbers (cdr numbers)) (print numbers) ;; ⇒ (10 20 30) (require 'cl-lib) (setq numbers (cl-adjoin 30 numbers)) (setq numbers (cl-adjoin 40 numbers)) (print numbers) ;; ⇒ (40 10 20 30)
2. write a macro
You can’t write a function that does the job, but you can still write a macro that simply expands to the previous workaround. Such a macro is usually called a modify macro. For the two problems, you don’t have to write such macros because Emacs Lisp already provide them:
(setq numbers (list 10 20 30)) (push 0 numbers) (print numbers) ;; ⇒ (0 10 20 30) (pop numbers) (print numbers) ;; ⇒ (10 20 30) (require 'cl-lib) (cl-pushnew 30 numbers) (cl-pushnew 40 numbers) (print numbers) ;; ⇒ (40 10 20 30)
You might want to check out cl-callf and cl-callf2 which help you write modify macros easily.
3. passing a symbol
You can write a function that takes a symbol from which the function can access the list. For the second problem, Emacs Lisp already provide such a function:
(setq numbers (list 10 20 30)) (add-to-list 'numbers 30) (add-to-list 'numbers 40) (print numbers) ;; ⇒ (40 10 20 30)
Such functions don’t play well with lexical scoping. So you shouldn’t use such functions with local variables
- - -
5. boxing and unboxing
You can write a function that takes a box-like object from which the function can access the list. The box-like object type you choose for this purpose can be anything that can contain a list. It can be vector, cons, or even list. This workaround will result in less readable code.
6. which workaround to use
We have seen many workarounds. I recommend the first workaround. The second workaround (macros) demands you to write macros, but why write a macro when you can just rely on the first workaround? The other two workarounds are like using double pointers in C. I don’t think use of double pointers in Lisp is an encouraged practice.
7. now the fourth problem: multiple return values
“What can I do when I want a function to return multiple results?”
You can just return multiple results as one list, in which case you might want to use cl-destructuring-bind from the caller side.
Or you can make the function take a callback function as an argument, and pass your multiple results as arguments to the callback function. In this case, you must turn on lexical scoping.
8. Common Lisp note
Common Lisp, unlike Emacs Lisp, supports multiple return values. It also has a radically different way of handling exceptions, different from Emacs Lisp and C and other languages.
I don’t’ think Common Lisp provides callf, callf2, add-to-list.