[net.lang.lisp] Beginners guide to Franz Lisp as promised

charlie (05/06/83)

I promised that when I completed a course and project in AI using
Lisp that I would post to the net some notes for novices.
Here it is. Thanks to all in net land who gave me advice.


               This collection of notes should help a novice Lisp programmer to
          get well started using Franz Lisp under UNIX 4.1BSD on the VAX.

          1) The first thing you must do is get some literature on the language
          itself. The new paperback 'LISP' by Patrick Winston is very good
          and quite compatible with Franz. See the appendix of this guide for
          a recommended reading list. Of course you should try to obtain a copy
          of "The FRANZ LISP Manual" which is about 1/2 inch thick. Mine is
          dated Sept 1981. It tells about the Liszt compiler, the Tracer
          and most of the functions but it has almost no examples. It is
          strictly for reference. The usual manual pages that are on line
          are nice but woefully inadequate for this large and complex system.

          2) Use your favorite editor to create a pgm.l file. The .l means
          lisp. My favorite editor is vi and there are lisp functions that
          can invoke vi from within a running lisp program. There is a lisp
          structure editor that can be invoked from within lisp but it
          seems like good advice for a novice to avoid using it.

          3) Start up lisp as follows, % is assumed to be the Unix prompt
                   %lisp
                   ->(load 'foo.l)
          The .l is not really needed but the single ' is. Later when you
          use the Liszt compiler it will make a foo.o file and if you merely
          (load 'foo) it will first try to get foo.o then foo.l and finally foo.

          4) For subsequent editing from within lisp just do this
                   -> (vil foo)
          It will invoke vi on the file foo.l and upon exiting vi it
          will load it into the lisp interpreter. This function is
          undocumented, thanks to some kind soul on the Usenet for
          telling me about it. Note that this function does not need
          the ' before the argument!

          5) Getting parenthesis to balance is tricky but here are two very
          useful things to know:
          [  ] automatically provides the correct number of balancing
          right parentheses. Also, in vi the % command will move the
          cursor to the paren or bracket that matches the one you currently
          have the cursor on. It works for both left and right parens.

          6) (exit) carriage return to leave the lisp interpreter.

          7) Comments appear after a semicolon on any lisp program line.
          Be careful not to comment out a line that causes right
          paren unbalance in what is left!

          8) You can freeze a session and restart later by
          (dumplisp filename) and just typing filename after the
          Unix prompt. This takes about half a megabyte of disk,
          a fair amount of time and is therefore not to be used lightly.


                                        - 1 -










          9) When your program runs, it will probably stop with some kind of
          error message and you will see the prompt <1>:
          additional errors cause deeper prompts <2>: <3>: etc.
          You can go up a level with cntrl-Z on my system, maybe cntrl-D
          on yours. If the error is correctable because you typed an
          atom wrong you can do a (return atom-spelled-right) i.e.
          spell it correctly and the lisp interpreter will take off
          and continue running. If it was a function name that
          was spelled wrong do a (return 'correct-func-name). The
          forced by putting the function (break) in your code, you
          will find the functions (showstack) and (baktrace) to be
          useful for debugging. To get it started after one of your
          (break) instructions just enter (return t).

          10) At first you will be amazed at how many functions seem to be
          built into the interpreter and you will wonder if there is some
          function or command that will give you a big list of all the
          currently loaded functions. I haven't found one! However
          you can use the pretty printer function (pp atom-or-func) to
          show if one exists. For example (pp sort) will return t
          machine coded, which means yes there is a function sort and it
          is compiled so it can't be pretty printed. This is one of those
          undocumented ones, more later. Also, at any break or error you
          can examine lists and functions easily by just typing
          -> (pp func) or (pp atom) where atom is a variable name that is
          currently bound to a list.

          11) The most useful debugging tool for me has been the Joseph
          Lister Trace package herewith called the tracer. I have not
          had quick success with the stepper or the debugger. The most
          common thing to do is enter
          -> (trace (func-name break))
          which will cause a break when that function is invoked. It
          will show the arguments on entry to the function and will show the
          result as the function exits. You may access the arguments
          with (arg 1) or (arg 2) etc. For example:
          T> (pp (arg 2))
          will pretty print the 2nd argument. the T> is the trace prompt.
          To get going again enter (tracereturn) or cntrl-Z. If you
          have a typescript going and cntl-z is the EOF on your system
          you better use (tracereturn) or the script will terminate.
          There are many other ways to get the tracer invoked but
          the one shown is sufficient. (untrace func-name) or
          a simple (untrace) to untrace everything will turn it off.
          If you do a (vil func) of a traced function it will no longer
          be traced when you come back from the edit. I'm not sure
          what happens.

          12) If you create a big data structure in the interpreter, or
          make alterations with the structure editor or if you just want to
          extract several pieces of a large program, then you can use
          the following to pretty print to a file
          ->(pp (F foo.l) prove smurf ok))


                                        - 2 -









          This will pretty print the functions prove smurf and foo out
          to file foo.l. The capital F is needed.

          13) There are times when you wish lisp had some kind of history
          mechanism. Well - ho ho - it does, again undocumented as far
          as I can tell unless you read the source code.
          * is bound to the last result you got back
          ** to the next to last
          *** to the last but two
          + is bound to the last thing you typed
          ++ next to last
          +++ to the 3rd from the last
          Needless to say these 'handles' are transitory and you should do
          ->(setq it *) and (pp it) if you want to hang onto something
          to play with for a while.

          14)  On my system and probably on yours can be found a wealth of
          lisp programs and functions in source code in /usr/lib/lisp.
          I must admit that as a novice I find most of them to be too
          complex but they are becoming more interesting as I progress.
          Beyond the Winston book there are some pretty hairy examples
          for complex problems in the paperback Techniques of Artificial
          Intelligence by Stuart Shapiro.

          15) The basic building block is defun which defines a function.
          If you need 'local variables' you need a prog inside the defun

          (defun foo
                   (arg1 arg2) ; arguments passed into function foo
                   (prog (local1 local2)
                   (setq local1 '(a b c))
                   .
                   .
                   (return local2)))

          An alternative to defun is def in which the arguments are
          declared a bit differently as follows:

          (def foo
                   (lambda (arg1 arg2)
                   ... body ... )) ; note that the right paren for the lambda is here

          A useful function is the undocumented sort function. It takes two
          arguments, a list of things to be sorted and a function of
          two args that returns t or nil depending on whether to sort
          them or not. Example:
          (sort '(1 2 4 3 7 6) '(lambda (one two) (greaterp one two)))
          which should return the list: (7 6 4 3 2 1).

          A very useful macro is the (repeat .. while .... until .... result).
          It not only allows initialized local variables, it provides looping control
          and can return a result. The result and one of the while or until
          keywords is optional. See appendix 2.



                                        - 3 -









          16) The liszt compiler can produce code that runs typically over ten
          times faster than the interpreter. It is a bit tricky to get things
          that include macros to compile however. There are a few things that
          help. In your program put (declare special variable) for all
          the variables that are global. Put all macros within an
          (eval-when (load compile eval) ......) function list.
          The .... may comprise several pages worth of your program! Then
          %liszt -x pgm.l > out. Much information is contained in the out file
          which you should look at. Successful compilation does not mean
          it will run however. Run time errors such as 'internal bad memory
          reference , suggest you (reset)' can occur. They may be understandable
          such as from a (member x y) where y is an atom and not a list or they
          may have something to do with macros that weren't compiled.
          The -x option produces a file that can be input to lxref for
          a cross reference which can be valuable for understanding a big
          program or even to find functions that arn't even referenced
          anymore!


          _A_p_p_e_n_d_i_x _A - _R_e_a_d_i_n_g _l_i_s_t

          1) LISP by Patrick Henry Winston. Addison-Wesley Publishing 1981.
          About $14. This is a very good introduction. Many examples
          and very readable. Solutions to the exercises are given.

          2) Techniques of Artificial Intelligence by Stuart C. Shapiro
          D. Van Nostrand Company. Paperback. Not in the Franz dialect
          but has complete examples of non-trivial problems.

          3) Artificial Intelligence Programming by Charniak, Riesbeck
          and McDermott. Lawrence Erlbaum Assoc. Publishers. 1980.
          Hardcover with lots of text and pieces of lisp code embedded in the text.
          Not Franz dialect.

          4) The Little Lisper by Daniel Friedman. Science Research Associates.
          1974. This 8 1/2 by 11 size thin paperback is a question and answer
          text. You cover up the right half of the page 'till you get the answer.
          It didn't hold my attention.

          5) BYTE magazine August 1979. The LISP issue.

          6) BYTE magazine September 1981. The Artificial Intelligence issue.

          7) Scientific American magazine Feb March and April 1983 in the
          Metamagical Themas column by Douglas Hofstadter on the "LISP genie".











                                        - 4 -









          _A_p_p_e_n_d_i_x _2 _S_o_u_r_c_e _o_f _r_e_p_e_a_t _m_a_c_r_o

          Originally from the Shapiro book but modified for Franz with
          local variables and a result key word by Mark Swartwout.


          ; repeat is a handy looping function. See the example below.
          ; The general form is (repeat (local val )"F1" "F2" ... "Fn")
          ; Local variables occur in the list after the repeat as pairs
          ; with the first atom being initialized to the 2nd item of the pair.
          ; Each argument is a form or a keyword i.e. while, until, or result.
          ; The forms are evaluated one after the other cyclicly until the form
          ; following an occurence of while evaluates to nil or the form after
          ; an until evaluates to non-nil. repeat then returns the value of this
          ; last form or if the keyword result is present, of the form after result.

          ;
          (def repeat
               (macro (call) (repeathelp (cdr call) (gensym 'l) (gensym 'v))))

          (def repeathelp
               (lambda (bodi lbl vbl)
                (cons 'prog
                      (cond [(null (car bodi))
                                    (cons (list vbl)
                                          (cons lbl (repeathelp1 (cdr bodi)
                                                                  lbl vbl (list 'return vbl))))]
                            [t      (cons (cons vbl (repeatvars (car bodi)))
                                      (cons (cons 'setq (car bodi))
                                          (cons lbl (repeathelp1 (cdr bodi)
                                                                  lbl vbl (list 'return vbl)))))]))))

          (def repeatvars
               (lambda (vars-n-vals)
                (cond [(null vars-n-vals)nil]
                      [t (cons (car vars-n-vals)
                               (repeatvars (cddr vars-n-vals)))])))

          (def repeathelp1
               (lambda (bodi lbl vbl tail)
                (cond ((null bodi) (list (list 'go lbl) 'exit tail))
                      ((eq (car bodi) 'while)
                       (cons (list 'cond
                                   (list (list 'not
                                               (list 'setq vbl (cadr bodi)))
                                         (list 'go 'exit)))
                             (repeathelp1 (cddr bodi) lbl vbl tail)))
                      ((eq (car bodi) 'until)
                       (cons (list 'cond
                                   (list (list 'setq vbl (cadr bodi))
                                         (list 'go 'exit)))
                             (repeathelp1 (cddr bodi) lbl vbl tail)))
                      ((eq (car bodi) 'result)
                             (repeathelp1 (cddr bodi) lbl vbl (list 'return (cadr bodi))))


                                        - 5 -









                      (t (cons (car bodi) (repeathelp1 (cdr bodi) lbl vbl tail))))))
          ;
          ;  An example of repeat
          ;
          ;(def rtest
          ;     (lambda nil
          ;      (repeat
          ;              (x 15 y 15)         ; local variables x and y are both set to 15.
          ;              (print (list 'x x))
          ;              (terpri)
          ;              (setq x (sub1 x))
          ;              while               ; stop and return nil
          ;              (greaterp x 0)      ;  when x is not greater than 0.
          ;              (setq y (add1 y))
          ;              until               ; stop and return t
          ;              (greaterp y 30)     ;  when y is greater than 30
          ;              (print (list 'y y))
          ;              (terpri))))
          ;
          ; This is what rtest expands to.
          ;
          ;(def rtest
          ;     (lambda nil
          ;      (prog (v00001 x y)
          ;            (setq x 15 y 15)
          ;     l00000 (print (list 'x x))
          ;            (terpri)
          ;            (setq x (sub1 x))
          ;            (cond ((not (setq v00001 (greaterp x 0))) (return v00001)))
          ;            (setq y (add1 y))
          ;            (cond ((setq v00001 (greaterp y 30)) (return v00001)))
          ;            (print (list 'y y))
          ;            (terpri)
          ;            (go l00000))))
          ;

tim (05/06/83)

Relay-Version:version B 2.10 gamma 4/3/83; site mhuxt.UUCP
Message-ID:<5172@unc.UUCP>
Date:Fri, 6-May-83 16:19:54 EDT


I wish I'd had something like that when I got started. There are of course
a number of things that could be added to it, but most important, I think,
is that vi has a "lisp" mode which provides automatic indentation of list
structures. It's not perfect -- it isn't specific to Franz, so it doesn't
know about semicolon comments and such -- but it sure helps. To use it,
just say ":set lisp" in vi. (Note: it is possible to compile vi without
lisp mode to save space. If this was done on your system, I'm sorry to
have wasted your time.)

Another handy vi mode is "showmatch". When this is on, entering a close
parenthesis causes the cursor to momentarily bounce to the matching
open parenthesis, if it's on the screen. Again, it's not perfect --
it doesn't know about square brackets -- but it is handy. To use it,
say ":set showmatch" or ":se sm" in vi.

You can set more than one option at once, for instance ":set lisp sm".
Finally, it is possible to set these options on the command line, so
you can use an alias to vi your Lisp programs with the modes turned on.

alias vl vi '+:set lisp sm' \!*

is my alias. It works with csh only, of course. Saying "vl foo.l"
starts up vi on the file foo.l with lisp and sm set. (Unfortunately,
you start at the end of the file instead of the beginning when you
do this, but that's no big thing.)

I hope this has served to help rather than confuse. Does anyone have
Gosling Emacs "electric-lisp-mode" working with Franz Lisp, by the way?

Tim Maroney

rogerh (05/07/83)

Yes, we have the Gosling Emacs electric-lisp-mode working.  I usually
have one window running Lisp, another editing the relevant function
definition file(s).  S'wonderful.  There are a couple problems: the
formatting, while better than the Franz Lisp pretty-printer, is not
perfect; and it gets real confused about the end of a function.  My
workaround for the second is Never begin a line with a close-paren.

I have a couple questions:

Does the "CMU toplevel" really exist?  I found the source, or at least
something that must be the source, but it don't load.

Has anyone messed with funargs?  It seems, on cursory examination, that
the tools are there to write a closure function -- a function to close
a functional form so that any free variables of the form are evaluated
in the environment extant at the point of application of the closure
function (whew!) -- but I can't find one in the manual.

	Roger Hayes
	University of Arizona
	arizona!rogerh
	
PS: We have Opus 36 of Franz Lisp;  I can't find a version number on
our electric-lisp.ml . rh.

tim (05/09/83)

I don't see how Franz Lisp, with its shallow binding, could efficiently
support funargs and closures. However, I could be wrong. Can anyone
provide a clear explanation of evalframe descriptors as described in
the functions eval and evalframe? You can call me a dummy if you like,
but please provide the answers too.

Tim Maroney
duke!unc!tim

george (05/12/83)

         "10) At first you will be amazed at how many functions seem to be
          built into the interpreter and you will wonder if there is some
          function or command that will give you a big list of all the
          currently loaded functions. I haven't found one!"


The function "oblist" returns a list of interned atoms.
Given this, it should be straight forward to write
a function that returns a list of atom names for functions.

		George Rosenberg
		duke!mcnc!idis!george
		decvax!idis!george