A quick tour of LispLand
:help - Prints a list of all available commands. :help <command> - Prints more detailed help on a specific command. :ld <file> - Loads a Lisp file. :cf <file> - Compiles a Lisp file. :cl <file> - Compiles and loads a Lisp file. :reset - Resets the Lisp interpreter after an error has occurred.
<cl> 12 <--- form typed at the Lisp prompt : just a number 12 <--- Lisp prints the result of the evaluation <cl> <--- you get a new promptSymbols are also forms, and by themselves they are usually associated with variable names. The results of evaluating a symbol that is bound to a variable is the value of that variable (to bind in Lisp is roughly to associate a symbol or name with a place where something can be stored):
<cl> *common-music-syntaxes* (:CLM :MUSICKIT :MIDI :CSOUND) <cl>The third and most important kind of forms are the....
(peter john williams) (1 2 3) (strawberry 23 red 1 4) (Robert (sane 1.77 58) rags ())As simple as that. Lisp excels at manipulating lists and so has lots of functions that can do almost anything to a list. And now comes the interesting part:
(+ 2 3)This is a three element list but also a very simple program written in Lisp. When the Lisp interpreter evaluates a list (or in other words tries to "execute" it), it assumes that the first element of the list is the name of a function to be called and the rest of the elements are the arguments that the function needs. So in the preceding case the function represented by the symbol "+" is called with two arguments that result from the evaluation of "2" and "3". As a number always evaluates to itself, the function "+" gets the numbers "2" and "3" as arguments and you hopefully get the correct result and a new prompt from the Lisp interpreter:
<cl> (+ 2 3) 5 <cl>It is important to note that the arguments to a function always get evaluated before being passed to it (of course unless you specify the reverse, as we will see shortly). So what happens if one of the arguments of this simple function call is not a number, but another list?:
(+ 2 (* 5 6))The same simple rules apply. The Lisp interpreter just treats each list as before. It calls the function with the result of evaluating its arguments. In this case the sequence is: the function "+" gets called with "2" as the first argument and with the result of evaluating "(* 5 6)" as the second. This second evaluation proceeds by calling "*" with the result of evaluating "5" and "6" which, being numbers, evaluate to themselves. The result of this intermediate step is of course "30" (check with your calculator if in doubt!) which is the number that gets sent to our first function ("+") as its second argument:
<cl> (+ 2 (* 5 6)) 32 <cl>Summarizing, when the interpreter evaluates a list it calls the first element as a function with the result of evaluating the rest of them as arguments. In other words always the operation first followed by the arguments.
(first (1 2 3))What's wrong with this? The problem is that an interpreter is a pretty dumb software creature, always playing by the same rules. When it evaluates the first and only argument of the function "first", it will try to use the number "1" as a function name with arguments "2" and "3", and of course you will get a nice error message.
<cl> (first (1 2 3)) Error: Funcall of 1 which is a non-function.  <cl>Lisp has no way of knowing that "(1 2 3)" is only a list of numbers and not another function to be evaluated. So you have to tell it what you want by quoting the argument:
(first '(1 2 3))The "'" character (quote) tells the Lisp interpreter that it must not evaluate the following form, but instead must use it "as-is". So now the interpreter calls the function "first" with the result of not evaluating the following list, and we get the expected result:
<cl> (first '(1 2 3)) 1 <cl>Note: After an error occurs the interpreter stops and awaits further commands as usual, but saves the context (variable values, pending function calls, etc) where the error occurred so that you can examine what happened in more detail. The level of error nesting is signalled by a number between square brackets before the <cl> prompt. You can use the :reset command to return the error nesting level to 0 and get rid of the pending context information.
<cl> :reset <cl>You should note that Lisp does not make any distinction between what is data and what is program. A list is a list and can be either data or a program... or even both! Things get really interesting when programs start manipulating or creating lists that are really programs. That is one of the reasons why Lisp was originally invented and has been the most widely used program for artificial intelligence applications.
When the first argument is a macro name the rest of the list gets shipped to the macro, which then decides by itself what to do with it. The purpose of a macro call is to return to the interpreter a program (actually a list) that is then executed in place of the original macro call. You can look to a macro as a small compiler that extends the Lisp syntax on the fly in ways that can make something easier to use from the human viewpoint.
And don't forget that the macro is a small compiler that decides by itself what to do with the arguments. Usually a macro does not evaluate its arguments! If in doubt look in the appropriate documentation! In the Common Music dictionary.wn documentation file, you will find at the right of each definition a description of whether it is a function, a macro or a variable.
<cl> (defun plus (a b) (+ a b)) PLUS <cl>The first element of this list is of course the name of the macro we are calling. The second is the name to be given to the new function (note that the macro defun, being an independent entity from the interpreter, decides what to do with its arguments and so it does not evaluate the name!). The third element in our list is the list of arguments that the function will receive when called. This is followed by an indeterminate number of forms that comprise the body of the function definition (in our example just one). These forms get executed in sequence when a call is made to the newly defined function. So if you now say:
<cl> (plus 4 (/ 10 3)) 22/3 <cl>you will find that the function just defined is available for use. Just to see the kind of syntactic sweetening that you get from using a macro, let's see what the Lisp interpreter really executes to create the new function:
<cl> (macroexpand '(defun plus (a b) (+ a b))) (PROGN (EXCL::SET-FUNCTION 'PLUS (FUNCTION (LAMBDA (A B) (BLOCK PLUS (+ A B))))) (REMPROP 'PLUS 'EXCL::%FUN-DOCUMENTATION) 'PLUS) T <cl>Macroexpand does just half of the work of a normal macro call. It returns the program that is equivalent to the macro call so you can examine it (the process of transforming the macro call to a normal Lisp program is called macro expansion). As you see the guy who wrote the defun macro saved you from having to deal with a lot of gory details!
<cl> (setf v1 '(1 2 3)) (1 2 3) <cl>What setf did was to bind the symbol "v1" to a place, where it stored the list (1 2 3). Now you can use v1 as a variable, and see that it evaluates to its contents...
<cl> v1 (1 2 3) <cl>...just as a function evaluates to its result (note again that the evaluation of v1 is (1 2 3)):
<cl> (first v1) 1 <cl>The first argument to setf is the name of a place. In that respect setf is quite powerful because you can specify almost anything where something can be stored, and the macro call will generate the appropriate Lisp code to modify the contents of that place. The second argument is the object to be stored, and there is no restriction on what it can be. Just keep in mind that setf evaluates the second argument, so you must use a quote to stop evaluation if necessary (see the preceding example).
You should note that the arguments to a function call get the same treatment. Each name declared in the function definition is bound to a temporary storage place that gets created when you call the function, and is initialized with the received value. The name can even be the same as that of another globally declared variable. In that case the value accessed by the code that is part of your definition is the locally declared value. The global variable gets shadowed by the local declaration. That is the reason why you should not think of a variable as the name. It is a place where things are stored, and the name is associated with a place depending on the context where the name appears.
(let ((x 12)(y 22) z) (format t "x = ~F~%" x) (format t "sum = ~F~%" (+ x y)) )In this example we define three variables: x, y and z with initial values equal to 12, 22 and NIL. The body of let consists of two calls to the format function, which prints some of the values. If you evaluate the following at the <cl> prompt...
(setf x 3.444)...and then again the previous example you will realize that when the body of let is being executed there are two different places holding values with the same name. The global place that you defined by executing the setf is shadowed by the local rebinding that let is doing. If you examine the global x afterwards you will find it still containing 3.444.
|©1995 Fernando Lopez-Lezcano. All Rights Reserved.|