No matter the Lisp dialect, it looks like every source code file containing Lisp functions isn't itself a list (the first time I was "surprised" by this was when working on my Emacs .el files).
I've got a few questions but they're all related to the same "issue" and it's probably just me misunderstanding a few things.
Is there a reason why source code files for the various Lisp dialects seems to be a bunch of "disorganized" functions like this:
(function1 ...)
(function2 ...)
(function3 ...)
Instead of a "Lisp list" of functions, maybe like this:
(
'(function1 ...)
'(function2 ...)
'(function3 ...)
)
I'm a bit surprised in this whole "code is data, data is code" thing to see that source code file themselves apparently aren't neat lists... Or are they!?
Are the source code files something you're supposed to "manipulate" or not?
What if I wanted to, say, convert one of my .clj (Clojure) source file to some CSS+HTML webpage, isn't it a "problem" that the source code file apparently isn't itself a list?
I'm beginning with Lisp so I don't know if my question makes sense or not and any explanation would be welcome.
In Common Lisp a source file contains lisp forms
and comments. Lisp forms are either data or Lisp code. Typical operations on a source file are done by the functions LOAD
and COMPILE-FILE
.
LOAD
would read forms from a file and execute them one by one.
COMPILE-FILE
is much more complex. It typically reads forms and compiles them to some other representation (machine code, byte code, C code, ...). It does not execute the code.
What would it help you if the file contain one list of forms instead of just multiple forms below each other?
Now for a example a compiler would read lisp forms from a file stream and compile them piece by piece.
If you want all forms you can do
CL-USER 170 > (defun read-forms (file)
(with-open-file (stream file)
(loop for form = (read stream nil nil)
while form
collect form)))
READ-FORMS
CL-USER 171 > (read-forms (capi:prompt-for-file "source file"))
((DEFPARAMETER *UNITS-TO-SHOW* 4.1)
(DEFPARAMETER *TEXT-WIDTH-IN-PICAS* 28.0)
(DEFPARAMETER *DEVICE-PIXELS-PER-INCH* 300)
(DEFPARAMETER *PIXELS-PER-UNIT* (* (/ (/ *TEXT-WIDTH-IN-PICAS* 6)
(* *UNITS-TO-SHOW* 2))
*DEVICE-PIXELS-PER-INCH*))
...
If you want to put parentheses around everything use PROGN
:
(progn
'form-1
(defun function-defintion-form () )
42)
PROGN
preserves also the 'top-level-ness' of its sub forms.
Side note: alternatives to this have been explored in Lisp for decades. The most prominent example is the now defunct Interlisp-D from Xerox. Interlisp-D was developed in parallel to Smalltalk by Xerox PARC. Interlisp-D originally used an structure editor to edit Lisp data and the source code was edited as such Lisp data. The development environment was based on this idea. But in the long run the 'source as text' won. Still you can emulate some of that in many current Lisp environments. For example many Lisp systems allow to write an 'image' of the current execution memory - this image includes all data and all code (also the compiled code). So you can work on this data/code and save an image from time to time.
In Lisp there are two levels of source code, or there is no source code at all depending on how you define source code.
The two levels are present because two separate conceptual steps are performed (normally) by a Lisp interpreter/compiler.
In this step the source code is a sequence of characters, for example coming from a file. Here the parenthesis, quoted strings, numbers, symbols, quote signs and even part of quasiquoting syntax is processed and transformed into Lisp data structures. At this level the syntax rules are about parenthesis, digits, pipes, quotes, semicolons, sharp signs, commas, at-signs and so on.
In this step the input are Lisp data structures and the output is either machine code, byte code or possibly the source is directly executed by an interpreter. At this level the syntax is about the meaning of special forms... e.g. (if ...)
, (labels ...)
, (symbol-macrolet ...)
and so on. The structure is uniform in Lisp code (just lists and atoms) but the semantic isn't (if
forms look like function calls, but they are not).
So in this view the question to your answer is yes and no. No for step 1, yes for step 2. If you consider only files then the answer is no... files contain characters, not lists. Those characters can be transformed by a reader into lists.
Why then someone says that Lisp has no syntax when in fact has two different syntax levels? The reason is that both of these levels are under the control of the programmer.
You can customize level 1 by defining reader macros, and you can customize level 2 by defining macros. So Lisp has no fixed syntax, and therefore a source file can begin with a "lispy" look and can end looking exactly like Python code.
A source file can contain anything (from a certain point on) because the initial forms could define some new reading rules that will change the meaning of following characters.
Normally Lisp programmers don't do crazy things with the reading level so most Lisp source code files look just like sequences of Lisp forms and they remain "lispy".
But this is not an hard constraint... for example I was not joking about Lisp syntax morphing into Python: someone did exactly that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With