0

This is quite basic question that started puzzling me recently while studying Lisp (and derivatives). I have read some Lisp books and web sites and this subject is somewhat obscured by other Lisp properties that seem to be having more meaning to the authors so they give them more attention. So I cannot find this subject in books and I have no one to ask to help me out figure it out, because I do not know anyone who 'speak' Lisp. So this forum seem to be appropriate place to ask.

Here's a question (explanation first):

Lisp program can be generally described as a list of s-expressions executed in sequence (in a file, including other source files loading). As far as I understand Lisp program generally looks like this:

(op data, data...)
(op data, data...)
(op data, data...)
(op data, data...)
(op)
.... ; presumably endless stream of s-expressions (like in 'repl')

whereby above s-expressions may take number of different forms (function definitions, variable declarations, branching and flow control etc...)

So, how come Lisp program is not defined as:

(program
   (op data, data...)
   (op data, data...)
   (op data, data...)
   (op data, data...)
   (op)
   ... ; presumably append-able list of s-expressions
)

?

It would seem logical and natural that program is a list of s-expressions to execute (as it is already implicitly understood just looks like missing explicit list declaration).

Is there particular (design) reason why first form is used and not the second? ( Or reasons are completely historical? Or ?)

Thank you!

  • 3
    Interactively a single line (...) can be given to the REPL, the LISP interpreter. (program (...) (...) ...) is not different from a single (op ...). So the reason is the interactiveness of a command line interpreter. – Joop Eggen Jul 2 at 13:21
  • 5
    What good is "program" doing anyone? Are you looking for something equivalent to main() in other languages? – candied_orange Jul 2 at 13:27
  • Thank you for reactions. I am still new to Lisp (which has complex system of variants with different ideas and implementations) so it is not that easy to learn. This is to say that there are surely many things that I still do not know. As mentioned it kind of looks like logical to me (and consistent). But your reaction give me more to think about. On one side how one uses LISP (tinker in REPL or running programs - both equally important) and on another side how LISP relates to modern understanding of structured source code. I understand some debates happened in the past (namespaces, modules..) – ljgww Jul 2 at 17:10
  • thank you (moderator or else) who changed the title to something meaningful. – ljgww Jul 2 at 17:12
  • Everything in Lisp is an S-expression. So, a program is also a single S-expression. In the imperative case, the expression can be a sequence of effectful operations; in many cases, it could be just one operation, like printing the result of a function invocation. – 9000 Jul 3 at 18:10
0

One of the reasons is that a single s-expression is slighty less practical in actual programming in many cases.

Remember, the evaluator in Lisp works like this: it reads a form and then evaluates it. The file compiler in Lisp works like this: it reads a form, then compiles it and creates the compiled code.

Now if we have a single form in a file, then it might have some drawbacks:

  • the form may be very large and the reader would return a very large data structure for the program

  • if there is a s-expression syntax error, just reading that large form creates an error. Thus we would not work with smaller expressions and have eventually an error, instead we would always get the error from the s-expression reader for the whole program up-front. That can be undesirable.

  • a program may want to manipulate the state of Lisp, such that the reader would read the following expressions differently. But that's not possible if all code is in an already read expression. Common Lisp for example has packages, which are symbol namespaces. Changing a namespace might for example import a symbol and one might later use that symbol as it was imported. These things would not be possible that way if we have a single s-expression for the code.

  • all your code would be already indented by some characters, by the usual Lisp code formatting conventions, which wastes horizontal space

Of course it is possible to write code in one expression, but as indicated above it is usually seen as less flexible and more problematic to edit in conventional text editors.

CL-USER 16 > (defmacro program (&body body) `(progn ,@body))
PROGRAM

CL-USER 17 > (program
               (defun foo (bar) (1+ bar))
               (defun baz (bar) (* pi (foo bar)))
               (+ (foo 10) (baz 20)))
76.97344572538566D0

Common Lisp was designed in such a way that:

  • there are top-level defining macros like defun, defmacro, defvar and defclass for individual definitions.

  • there are special macros and functions influencing the execution and compilation, which are not nesting: in-package, proclaim, ...

  • there are some operators which are influencing execution and compilation, which are nesting: progn, eval-when, ... But these are usually not used to group large amounts of code - but sometimes in code which is generated by Lisp.

  • First and foremost - Thank you for valuable insights! Particularly, I was convinced even before I asked this question that the structure I was pondering could be somehow made/simulated in Lisp itself, just had no idea how (do not know that much). The example that starts from what the pondering was - with a simulation of it - tells and teaches a lot. Commentary adds some sugar on top. I am not sure that there is anything else to add to this but I am inclined to mark this as an answer to the question. At least it is as such to me. – ljgww Jul 5 at 18:57
  • 1
    I would like however to leave another note. Lisp books would be much more helpful if they classify sets of s-expressions (later I learned they are called like that because they are 'symbolic' expressions). I know that there is something I can call 'standard' S-exp which is in the form (op list...) which evaluated every member of list (if present) and then do the op. But, not all S-Exps do the same. This is not stressed enough. We shall learn what they are in the concept before we learn what they do. I believe this would be helpful. IMHO. Maybe that becomes obvious by the time. – ljgww Jul 5 at 19:02
  • @ljgww: right, symbolic expressions. The 'standard' s-expressions are called 'function forms', where the first operator is a function name and the other elements are arguments, which are in Common Lisp evaluated left-to-right. The other forms are usually called 'macro forms' and 'special forms' (using built-in operators with special behavior). So if we look at an operator, we also need to know if it's a function, a macro (and what kind of macro) or one of the special operators. The Common Lisp standard defines it in detail. A good Lisp book should explain that, too. Important, as you say. – Rainer Joswig Jul 5 at 19:38
  • Taking note on proper terminology. TY @Rainer (intended as a joke not as question: why it is s-expression and not symbolic-exp? - its about same length :) ) – ljgww Jul 6 at 21:13
1

It would seem logical and natural that program is a list of s-expressions to execute (as it is already implicitly understood just looks like missing explicit list declaration).

Only if you think of programs as lists of orders to execute like a recipe.

That is not how lisp was designed, and not the mental model for functional programming at all. There, it is logical and natural that a program is a function - very often comprised of progressively smaller functions. You give it some input, it gives you some output. Very mathematical, and very reasonable for the people of the time that knew of computers as things that compute.

Though lisp the language wasn't really designed so much as McCarthy took Lambda Calculus and implemented it. Lambda Calculus in turn wasn't designed for computers at all (since it predated electronic computers by a few decades). It was trying to express the simplest sort of foundation for computation in general. It's intuitive for us to think about lists as lay-people, but they don't really exist in mathematics.

  • Thank you for pointing out the way of thinking, specially functional way of thinking. This explains my understanding struggle. Having majority of programming paradigm revolving around sequence of execution I forget the math origin (by now programming and math look so much apart). I agree that in math the sequence of geometric or logic theorem proof or resolving numerical analysis task is not important - execution of tasks and sub-tasks - as much as it concentrates on resolving the task (apart from the optional step of setting up the theory how to resolve). – ljgww Jul 2 at 19:38
  • Generally functions are also the typical 'procedures' of other programming languages, since Lisp does not force the developer to write 'functional' code. One can use imperative control flow, imperative state change for side-effects, etc. – Rainer Joswig Jul 5 at 19:44
-1

I think a big part of it is that there isn't an overall sequence to top-level code in most cases. Sure, maybe the inside of a function is sequential, but the code as a whole is not, and the order of functions usually isn't significant.

  • History is level 2 chaos system. We are notoriously bad at pondering of its causality - predicting the future as much as explaining the past. If you mount history in programming you can see causality of sequence of events, but this is not what is pondered as important as system is seen as state at the given point in time. Order of definition is not important only if there is no dependence (as it seems to be a reality to most interpreters, compilers would typically do 2 different approaches to circumvent this issue). As long as system is seen in the moment in time the argument holds its logic. – ljgww Jul 3 at 23:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.