What is code, or more specifically, source code? As usual, Wikipedia leaves no doubt:
In computer science, source code is text written in a computer programming language.
Curiously, the Common Lisp HyperSpec disagrees:
source code n. code representing objects suitable for evaluation (e.g., objects created by read, by macro expansion, or by compiler macro expansion).
where code is defined as follows:
code n. 1. Trad. any representation of actions to be performed, whether conceptual or as an actual object, such as forms, lambda expressions, objects of type function, text in a source file, or instruction sequences in a compiled file. This is a generic term; the specific nature of the representation depends on its context. 2. (of a character) a character code.
Indeed, Common Lisp may be the only moderately popular language that does not define code as a piece of program text. So, while the situation seems to be pretty clear-cut in other languages, what is source code in the sense that the HyperSpec understands it, and why is it a useful concept?
Code is Data
As per the HyperSpec, source code is (some representation or instance of) a set of objects “suitable for evaluation.” Apparently, therefore, Lisp source code is, by definition, Lisp data. Now, what kinds of data objects are “suitable for evaluation?” The answer may surprise you: all of them!
Indeed, any object is evaluable. Objects intended for evaluation are called forms, which the HyperSpec classifies into three types: symbols, conses, and self-evaluating objects. All objects that are neither symbols nor conses are, without exception, self-evaluating objects.
This means that the following forms are all well-defined:
(eval (list 'print "Hello world!")) ;prints “Hello, world!” (eval 100) ;=> 100 (eval (make-hash-table)) ;=> #<a hash table>
Though it might seem ridiculous, hash tables, structs, and class instances are actually code as per the HyperSpec. Their usefulness as code may be restricted, since they merely evaluate to themselves. However, there are situations in which embedding objects in code is pretty darn useful. Consider the following code, for instance:
(defun find-mulkey-word (string) (scan "([Mm]ulk|[Kk]lum|Munoca)" string))
That's just a simple function call there, you might say. Right you are. However, a function may optionally be overloaded with a compiler macro that the compiler may choose to invoke in order to transform function calls involving the function. Through this, the above example actually expands to the following:
(defun find-mulkey-word (string) (scan (load-time-value (create-scanner "([Mm]ulk|[Kk]lum|Munoca)")) string))
Now what the hell is that supposed to mean, you ask? That, my friends, is an example of optimization by load-time evaluation. At load-time (that is, when the code above is loaded into the Lisp runtime), the create-scanner
form is executed. It compiles the regular expression into a ready-to-use scanner object. This object then replaces the load-time-value
form in the code. As a result, the scanner is only created once instead of every time find-mulkey-word
is called (which is a lot of times, clearly, since it's such a useful function).
What's that? I can do this in C with a static variable, you say? Sure you can. Except it's you who has to worry about it. In Lisp, it's the library author. You don't even have to muck around with “objects” and useless crap like that—just write it as a simple, honest-to-God function call, because that's what it is, right? Don't you think that's at least a tiny bit nicer? Yeah, I think so, too.
Comments
Submit a comment
Note: This website uses a JavaScript-based spam prevention system. Please enable JavaScript in your browser to post comments. Comment format is plain text. Use blank lines to separate paragraphs.