double pointers and Lisp lists

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: cons and cl-adjoin and cdr.

(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: push and cl-push-new and pop.

(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

4. - - -

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.

This entry was posted in Lisp and tagged , , . Bookmark the permalink.

2 Responses to double pointers and Lisp lists

  1. Pingback: Living with Emacs Lisp | Yoo Box

  2. Pingback: Emacs Lisp lexical binding gotchas and related best practices | Yoo Box

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s