Common Lisp environments include a read-eval-print loop that is also known as “REPL”. The interactivity of the REPL is cool, but there’s at least one reason why the “R” part of the REPL rules.
The “reader” of Lisp can slurp an arbitrarily complex expression, and return an object that is directly usable as any other Lisp object!
We can create two objects that are not EQ but have EQUAL representations in the toplevel REPL loop:
CL-USER> (defparameter *X* (list 1 2 3)) *X* CL-USER> (defparameter *Y* (list 1 2 3)) *Y* CL-USER> (eq *X* *Y*) NIL CL-USER> (equal *X* *Y*) T
The *X* and *Y* objects are not equal as far as EQ is concerned, because LIST creates new CONS cells for each one of them. But their representations are EQUAL, because they are lists with the same number of items and each successive item is EQUAL to the same item from the ‘other’ list.
Lisp can store the printable representation of an object in a string too:
CL-USER> (defparameter *X-STRING* (format nil "~A" *X*)) *X-STRING* CL-USER> *X-STRING* "(1 2 3)"
But then the real magic starts happening when you read back an object from that string representation, and start using it as a fully functional Lisp object:
CL-USER> (defparameter *NEW-X* (read-from-string *X-STRING*)) *NEW-X* CL-USER> *NEW-X* (1 2 3) CL-USER> (eq *X* *NEW-X*) NIL CL-USER> (equal *X* *NEW-X*) T CL-USER> (mapcar #'1+ *NEW-X*) (2 3 4) CL-USER>
Note how we didn’t really have to care about how a list can be converted to a string buffer. Neither did we have to care about allocating enough memory for that buffer, or to write specialized printer functions for each element of the list. What would take at least a few dozen lines in C or another, simpler language is already part of Lisp. And it’s been there for a very long time.
That’s one of the reasons why the “R” (the reader) part of the REPL rules :-)