I wanted to read a function from a string, and apply an argument to it. This seemingly simple task is surprisingly tricky in Lisp-like languages. The basic steps are straightforward: read the contents of a string, parse them as a function name, and apply the corresponding function to the desired arguments. Lisp-based languages are homoiconic, and also tend to have support for read-time evaluation. The combination makes safe parsing of data structures both powerful and dangerous.
I tried to rely on my prior Common Lisp knowledge, but that turned out to be information I needed to unlearn. There might still be errors in my understanding. And, while my writeup is accurate as of Clojure 1.5.1, the language is still fast evolving. Take the information here with a grain of salt.
Of Symbols and Namespaces
While reading, one has to deal with symbols, as they are invariably part of the input stream. In Common Lisp (CL) a symbol always belongs to a package, except when it doesn't. Every package symbol is automatically interned. So two symbols with the same name are always identical. The exception is that you can create uninterned symbols that don't belong to any package. And these symbols aren't equal to each other even when they have the same name.
Clojure is different. Instead of packages we have namespaces, and quoted
symbols aren't automatically interned into namespaces. You can ask
for a map of all the interned symbols. Quoting will give the uninterned
symbol, even if there's an interned symbol with the same name. There's
some more information and examples in this
google groups post. The CL transcript above can be approximated in
The keyword namespace in clojure is different, and symbols in that namespace have a distinct appearance. This is also the case in CL, and clojure's behavior more closely resembles that of CL.
- All keyword symbols begin with a
- Keywords are automatically interned. One needn't explicitly create an interned keyword.
- A corollary is that two keywords with the same printed representation will always be identical.
Keywords have a special role in clojure, in their ability to access content in data structures. These properties are essential in enabling that role.
One of the read-time pitfalls possible in CL is interning a very large number of symbols into a package, which is effectively a memory leak. Symbols are garbage collected only when they're uninterned. And the automatic interning of symbols is a subtle bug that can creep into many programs. This concern isn't realized in clojure for two reasons:
- Symbols aren't automatically interned, except keywords.
- Interned symbols are based on interned strings, so can be garbage collected when there are no references to them.
The Pitfalls of
Another type of problem occurs during read-time evaluation. The reader can execute code as its reading an s-expression from a stream. Here's a far better writeup on the matter than I could have done.
Add clojure.tools.reader to your project.clj. Then the following works: