question mark and Emacs Lisp

This article tries to answer:

  • How does elisp reader process question marks?
  • When and how should you to escape question marks in your elisp code?

1. motivation

Processing of question marks can seem mysterious to some.

For example, take this code:

(require 'dash)
(-contains? '(1 2 3) 1) ;; => t
(-contains? '(1 2 3) 2) ;; => t
(-contains? '(1 2 3) 4) ;; => nil

That function’s name ends with a question mark and the question mark is not escaped. If you call -contains? without first loading dash.el you get this error:

Symbol's function definition is void: -contains\?

Interestingly, the error message escapes question marks in function names.

Now take this example:

(rx "Finding " (? "N") "emo") ; ⇒ "Finding N?emo"

No escape of the question mark there.

Guess what would happen if you evaluate the following expression for curiosity.

(? "N")

You might expect to get this error:

Symbol's function definition is void: \?

You get this error instead:

Invalid function: 32

So this makes you curious about what’s going on.

2. short answer

At least one thing is obvious: you don’t need to escape question marks in strings, or in comments, or when a function name ends with a question mark.

;; la?
(la? "la?")

Other than that, you are supposed to escape question marks, usually.

3. some background

In Emacs Lisp, the question mark is usually used for character literals. For example, ?a denotes the character a, and this is the same as its character code 97.

(equal ?a 97) ; ⇒ t

(elt "aye" 0) ; ⇒ 97

(char-to-string ?a) ; ⇒ "a"
(char-to-string 97) ; ⇒ "a"

In other words, the read syntax for character a is ?a.

You can guess what would be the read syntax for the space character and for the question mark character itself:

(char-to-string ? ) ; ⇒ " "
(char-to-string ??) ; ⇒ "?"

(let ((space ? )
      (question ??))
  (message "[%c][%c]" space question) )
; ⇒ "[ ][?]"

If you feel uncomfortable using such literal syntax for space and question mark, consider using their character codes instead like this:

(let ((space 32)
      (question 63))
  (message "[%c][%c]" space question) )
; ⇒ "[ ][?]"

4. escaping question marks in symbol names

If a symbol name starts with a question mark, you need to escape it. If a question mark is in the middle or at the end of the symbol name, escaping or not doesn’t matter and so you don’t need to escape it.

;; no need to escape
(quote (o?o? o??o??)) ; ⇒ (o\?o\? o\?\?o\?\?)

;; escaping or not doesn't matter
(equal '(o?o? o??o??)
       '(o\?o\? o\?\?o\?\?))
; ⇒ t

;; escaping or not doesn't matter
(symbol-name 'o?o?) ; ⇒ "o?o?"
(symbol-name 'o\?o\?) ; ⇒ "o?o?"

;; question marks as first letters should always be escaped.
(quote (\?o? \?o?o?)) ; ⇒ (\?o\? \?o\?o\?)
(symbol-name '\?o?) ; ⇒ "?o?"

5. what happens if I don’t escape question marks that are first letters?

;; do this
(quote (\? \? a)) ; ⇒ (\? \? a)

;; don't do this
(quote (? ? a)) ; ⇒ (32 32 a)

(32 32 a) is probably not what you want. Why does it return (32 32 a)? (hint: what was the read syntax for the space character?)

Evaluating the following will lead to parsing error.

(quote (a a ?))

Why? (hint: what would be the read syntax for the closing parenthesis character?)

6. what about rx forms?

The rx macro is designed in such a way that you don’t have to escape the question marks.

(rx "Apocalypse No" (?? "w"))

7. Common Lisp note

In case you are curious about how Common Lisp handles question marks.

In short, Common Lisp doesn’t make you have to escape question marks. See the following comparing code.

Emacs Lisp:

(cl-loop for sym in '(:0 :1 \a\b\c abc
                         :2 \n ha? \? o
                         :3 o?o? o??o?? o\?o\? o
                         :4 \?o \??o \?\?o o)
         for name = (symbol-name sym)
         collect name)(":0" ":1" "abc" "abc"
   ":2" "n" "ha?" "?" "o"
   ":3" "o?o?" "o??o??" "o?o?" "o"
   ":4" "?o" "??o" "??o" "o")

Common Lisp:

(loop for sym in '(:1 \a\b\c abc
                   :2 \n ha? \? o
                   :3 o?o? o??o?? o\?o\? o
                   :4 \?o \??o \?\?o o
                   :5 ?o ??o o)
   for name = (symbol-name sym)
   collect name)

=> ("1" "abc" "ABC"
        "2" "n" "HA?" "?" "O"
        "3" "O?O?" "O??O??" "O?O?" "O"
        "4" "?O" "??O" "??O" "O"
        "5" "?O" "??O" "O")
This entry was posted in Emacs and tagged . Bookmark the permalink.

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