This manual documents the LOOPS, Berkeley Logo Object Oriented
Programming System, written entirely in Logo, which runs as a
subsystem under Berkeley Logo.  The manual is divided into the
following chapters:

Introduction
************

Overview
========

Copyright (C) 2000 by Hrvoje Blazevic

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at
your option) any later version.

This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

This program is still being written. It started as one of those, SEE
IF I CAN DO IT projects, and although it has grown considerably since
then, it is still just a `very' rudimentary OOPS. The basic ideas
used in development came from Scheme_and_the_Art_of_Programming,
therefore its attempt to add Lisp features to Logo should not come as
a surprise. This manual assumes that you already know how to program
in Logo, and merely describes the additional features, and their use.
Implementation itself is not discussed in detail, but as the code is
available for inspection, it should be easy to modify and improve
upon. This EASY is meant as EASY, after heaving read
*Computer_Science_Logo_Style, Volume_1_2_3 by Brian Harvey* (MIT
Press, 1997).

LOOPS code consists of four different parts, different in the way
they interact with Logo, and in the way they are loaded. First two
are the changes to some standard Logo procedures, and additions to
Logo, including Lisp structures COND, CASE and LETREC, with the
compiler for these structures. Third part is the actual LOOPS code.
All three are in the .loops file, normally residing in your home
directory, that must be loaded manually, or through Emacs logo-mode.
The fourth part are the additions to Logo library, which are loaded
in the standard way, on demand, just like the rest of ucblogo
library. Without some of these additions, LOOPS will not work, but
all are useful in their own right.

When .loops is loaded, behavior of Logo changes, and although it will
still run standard Logo programs, it also allows programs to be
written in a new style. Here is a brief list of changes:

     Any value is TRUE except FALSE; (affecting IF AND OR NOT TEST WHILE ...)

     Serial AND OR .AND

     Local procedure definition; (LETREC Scheme special form, METHOD.EXPORT)

     Lisp control special forms COND CASE

     Compiler for special forms; (LOOPS.COMPILER)

     Object compiler; (OBJECT.MAKER + set of auxiliary procedures)

     Set of base classes for OOP.

For those that have experience with any REAL OOPS, do not expect to
find Smalltalk-80 here. When designing LOOPS, I was primarily
concerned with HOW to implement it then WHAT can it practically do.
On top of that I have very limited experience with OOP.

Unfortunately, all this is expensive. Programs will run slower than
with standard Logo. Another detracting side effect is that normally
very informative Logo error messages, became totally useless if
caught within the scope of LOOPS high level procedures, or new
versions of logical procedures.

I have tried to make this as safe as I could. You should still be
able to run and test standard Logo programs, and even write new
programs that will behave in a standard way. Nonetheless, note--LOOPS
is *not* thoroughly tested!

Entering and Leaving LOOPS
==========================

LOOPS was developed and tested only on Free-BSD and GNU-Linux. For
non UNIX OSs, I only assume that this is the right way to start.

The process to start LOOPS depends on your operating system:

`DOS:'
     Change directories to the one containing Logo (probably
     C:\UCBLOGO).  Then type UCBLOGO for the large memory version.
     LOOPS will not run on 640K. When Logo is running, load LOOPS.LG
     file with LOAD command. You will have to copy additional library
     files manually to the directory which holds standard Logo
     library. However I have never tried this, so--chances are, LOOPS
     will not work on DOS at all.

`Mac:'
     Double-click on the LOGO icon within the "UCB Logo" folder, and
     load loops.lg file with LOAD command--I hope? You will have to
     copy additional library files manually to the directory which
     holds standard Logo library.

`Windows:'
     Double-click on the UCBWLOGO icon in the UCBLOGO folder. When
     Logo is running, load LOOPS.LG file with LOAD command. To exit
     LOOPS and restore standard ucblogo, type RESTORE.STANDARD
     command. You will have to copy additional library files manually
     to the directory which holds standard Logo library. However,
     working with LOOPS in Windows will be difficult due to the Lisp
     like structure of LOOPS code. You REALLY need an editor (like
     *Emacs*) that recognizes Logo syntax and indentation rules.
     Unfortunately, although Emacs is available for Windows,
     LOGO-MODE is not. You might try using LOOPS with MSWLogo--it
     should work, but you still have the same problem with the
     editor--it is simply not good enough.

`Unix:'
     Type the word logo to the shell.  (The directory in which you've
     installed Logo must be in your path.) then load .loops file with
     LOAD command. These are just first aid instructions. The right
     way to do this follows:

     Install *Emacs logo-mode* (v2.7 or later). This will
     automatically install all parts of LOOPS in proper directories.
     Library additions will be added to /usr/local/lib/logo/logolib,
     their help files to /usr/local/lib/logo/helpfiles and .loops
     into your home directory. Your .emacs file will also be updated
     accordingly. On completion of installation, you will be offered
     to work through logo-mode tutorial--do it. All of LOOPS
     features, including starting and exiting cleanly, are available
     from Emacs menus in logo-mode.

Changes to Standard Logo Procedures
***********************************

One of the differences between Logo and other Lisp dialects, and the
one that affects programming style, is what Logo accepts as true.
Logo's idea of truth is only the word `"true'. Likewise, false is
only the word `"false'. Anything else--handed as the first input to
control structures results in error.  Therefore the base change
introduced in loops is: ALL RETURNED VALUES ARE CONSIDERED TRUE,
EXCEPT THE WORD `"false'. Word `"true' has no special meaning in this
context. Following control structures have been altered to accept
this: IF, IFELSE, and TEST. IFTRUE, IFFALSE, WHILE, UNTIL DO.WHILE,
and DO.UNTIL have not been altered, but work nonetheless by virtue of
using internally either IF or TEST.

Control
=======

if
--

     IF anyvalue instructionlist
     (IF anyvalue instructionlist1 instructionlist2)

command.  If the first input has any value except FALSE, then IF runs
the second input.  If the first input has the value FALSE, then IF
does nothing. (If given a third input, IF acts like IFELSE, as
described below.)  It is NOT an error if the first input is not
either TRUE or FALSE.

For compatibility with earlier versions of Logo, if an IF instruction
is not enclosed in parentheses, but the first thing on the
instruction line after the second input expression is a literal list
(i.e., a list in square brackets), the IF is treated as if it were
IFELSE, but a warning message is given.  If this aberrant IF appears
in a procedure body, the warning is given only the first time the
procedure is invoked in each Logo session.

Standard Logo IF is renamed IF.TF

ifelse
------

     IFELSE anyvalue instructionlist1 instructionlist2

command or operation.  If the first input has any value except FALSE,
then IFELSE runs the second input.  If the first input has the value
FALSE, then IFELSE runs the third input.  IFELSE outputs a value if
the `instructionlist' contains an expression that outputs a value.

Standard Logo IFELSE is renamed IFELSE.TF

test
----

     TEST anyvalue

command.  Remembers its input, which can be anything, for use by
later IFTRUE or IFFALSE instructions.  The effect of TEST is local to
the procedure in which it is used; any corresponding IFTRUE or
IFFALSE must be in the same procedure or a subprocedure.

Standard Logo TEST is renamed TEST.TF

.and
----

     (.and instructionlist)
     .and instructionlist1 instructionlist2
     (.and instructionlist1 instructionlist2 ...)

operation. Runs `instructionlist's one by one until one returns
FALSE, in which case .and immediately returns FALSE. If none return
FALSE, .and outputs the result of last `instructionlist'. The only
difference with AND, AND THAT IS THE REASON FOR CLASSIFYING .AND AS A
CONTROL STRUCTURE is that .AND accepts even *nothing* as TRUE.
Therefore `instructionlist' does not have to return any value, and
still be accepted as TRUE.

In the code that follows, the third instructionlist `[make :var1 text
:var]' will be accepted as TRUE:

     (.and [wordp :var] [definedp :var] [make :var1 text :var] ...)

If last `instructionlist' does not output, word `#unspecified' will
be returned.

Logical Operations
==================

Both--Standard Logo, and LOOPS logical operators `and' and `or' use
same names, but they differ in the range of syntax they accept.
Standard Logo `and' and `or' MUST have words, as first inputs (this
is rather inaccurate description--they actually accept ONLY words
`true' and `false'), while LOOPS `and' and `or' accept lists and/or
words as inputs. This is also inaccurate--if you want to use LOOPS
functionality (serial execution and any value accepted as `true') you
MUST quote (protect with square brackets) all inputs. To preserve
compatibility with standard Logo code, LOOPS `and/or' accept also
words as inputs, but then Logo rules must be adhered to; value of
inputs MUST be only words `true', and `false'. LOOPS `and/or' accept
even mixed syntax of words and lists as inputs, however this is
discouraged.  See CODE EFFICIENCY.

As LOOPS `and' and `or' do not necessarily evaluate ALL of their
inputs, they can be used as control structures, just like in Lisp.
`or' returns immediately on first success. `and' returns immediately
on first failure.

However, note that (unlike Logo and LOOPS IF and TEST), both Logo and
LOOPS OR AND and .AND , can't properly handle code with side effects,
and results can be unexpected. Take a look at following two
procedures:

     to and.logo.test
     pr namep "a
     pr and run [localmake "a 1 pr :a "true] namep "a
     end

     to and.loops.test
     pr namep "a
     pr and [localmake "a 1 pr :a "true] [namep "a]
     end

and.logo.test is using standard Logo AND, while and.loops.test is
using LOOPS AND . When run, both will give following output:

     ? and.logo.test
     false
     1
     false

     ? and.loops.test
     false
     1
     false

Obviously, in both cases, the block of code with side effects
[localmake "a 1 pr :a "true], had no visible effect outside that
block, otherwise the third line of output should have been TRUE in
both cases.

and
---

     Logo:
     AND tf1 tf2
     (AND tf1 tf2 tf3 ...)

     LOOPS:
     (AND)
     (AND instructionlist)
     AND instructionlist1 instructionlist2
     (AND instructionlist1 instructionlist2 ...)

LOOPS version of AND is a simulation of AND special form--that is it
does not evaluate all of its inputs if not needed. It starts
evaluating from left to right, and returns FALSE immediately after
one of the inputs evaluates to FALSE. If none of inputs evaluate to
false, the value of the last input is returned.

All `instructionlist's MUST be operations. If you want to mix
operations and commands use .AND instead.

Standard Logo AND is renamed AND.TF Use AND.TF only on speed
sensitive code where serial evaluation is not required.

or
--

     Logo:
     OR tf1 tf2
     (OR tf1 tf2 tf3 ...)

     LOOPS:
     (OR)
     (OR instructionlist)
     OR instructionlist1 instructionlist2
     (OR instructionlist1 instructionlist2 ...)

outputs whatever is returned by the first successful evaluation of
`instructionlist', or the word FALSE if all `instructionlist's fail.
All `instructionlist's MUST be operations.

Standard Logo OR is renamed OR.TF Use OR.TF only on speed sensitive
code where serial evaluation is not required.

not
---

     NOT anyvalue

outputs TRUE if the input is FALSE, and FALSE if the input is any
value but FALSE.

Standard Logo NOT is renamed NOT.TF

Extended Prefix Operators
=========================

There are some minor changes to following prefix operators:
DIFFERENCE QUOTIENT BEFORE? EQUAL? GREATER? and LESS? . They now
accept more than two arguments. However note that BEFOREP EQUALP
GREATERP and LESSP have NOT been changed. Only those ending in `?'
are changed.

Logo DIFFERENCE and QUOTIENT are still available under following
names: `difference.s' and `quotient.s'

I am not sure how useful this is. What I REALLY wanted to do was to
change Logo infix operators `+ - * /' to prefix versions, so that
following syntax could be used:

     print (+ 1 2 3 4 5)     => 15

This can easily be done for `+ * /', but `-' is a problem. Logo
parser is handling `-' differently than other operators, so--if I
can't change `-', I decided not to change anything. It would have
been nice though, to be able to write `(- 5 2 1)', than to have to
write `(DIFFERENCE 5 2 1)'.

difference
----------

     (difference number)
     difference number1 number2
     (difference number1 number2 ...)

operation. Outputs -NUMBER, or NUMBER1-NUMBER2- ...  Logo
`difference' is available as `difference.s'

quotient
--------

     (quotient number)
     quotient number1 number2
     (quotient number1 number2 ...)

operation. Outputs 1/NUMBER, or NUMBER1/NUMBER2/...  Logo `quotient'
is available as `quotient.s'

before?
-------

     (before? word)
     before? word1 word2
     (before? word1 word2 ...)

operation. Outputs `true' or `false':

     (before? "a)            => true
     before? "a "b           => true
     (before? "a "b "c "c)   => false

beforeq?
--------

     (beforeq? word)
     beforeq? word1 word2
     (beforeq? word1 word2 ...)

operation. Outputs `true' or `false':

     (beforeq? "a)            => true
     beforeq? "b "a           => false
     (beforeq? "a "b "c "c)   => true

equal?
------

     (equal? input)
     equal? input1 input2
     (equal? input1 input2 ...)

operation. Outputs `true' or `false':

     (equal? 1)             => true
     equal? "a "b           => false
     (equal? 1 1 1)         => true

greater?
--------

     (greater? number)
     greater? number1 number2
     (greater? number1 number2 ...)

operation. Outputs `true' or `false':

     (greater? 1)             => true
     greater? 1 1             => false
     (greater? 3 2 1)         => true

greatereq?
----------

     (greatereq? number)
     greatereq? number1 number2
     (greatereq? number1 number2 ...)

operation. Outputs `true' or `false':

     (greatereq? 1)             => true
     greatereq? 1 2             => false
     (greatereq? 3 2 2)         => true

less?
-----

     (less? number)
     less? number1 number2
     (less? number1 number2 ...)

operation. Outputs `true' or `false':

     (less? 1)             => true
     less? 1 1             => false
     (less? 1 2 3)         => true

lesseq?
-------

     (lesseq? number)
     lesseq? number1 number2
     (lesseq? number1 number2 ...)

operation. Outputs `true' or `false':

     (lesseq? 1)             => true
     lesseq? 1 1             => true
     (lesseq? 1 2 1)         => false

Additions to Logo
*****************

The main reason for these additions being here, and not in the
additions to Logo Library--is that they all require COMPILER to
achieve reasonable performance. Although (e.g., CASE) itself can be
loaded from the library on demand, compiler can not. Emacs logo-mode
needs the whole block of code loaded before it will display menus to
operate the compiler.

Code Efficiency
===============

When talking about performance--first, it should be obvious that
programs written with LOOPS code (but without using OOPS--that is
another story altogether) WILL run slower than code written in
standard Logo. However, in certain situations, even simple LOOPS code
can match, and at times even beat (by order of magnitude) standard
Logo. By SIMPLE, I mean the code written by a beginner. It is should
be obvious that the same level of performance MUST be achievable in
standard Logo as well, it is just unlikely, that a beginner will know
how to. Let's have a look at one such extreme case:

The problem consists of stepping down a very long list and performing
a time consuming operation on each item. This TIME CONSUMING
operation returns either a value, or "false. If a value is returned,
this value should be the final result of the procedure.

This is a simple version written in Logo:

     to speed :list
     if emptyp :list [op "false]
     local "tmp
     make "tmp very.expensive.operation first :list
     if not equalp :tmp "false [op :tmp]
     op speed bf :list
     end

This one is written in loops code:

     to speed :list
     condc [
        [[emptyp :list] [op "false]]
        [[very.expensive.operation first :list]]
        [else [op speed bf :list]]]
     end

On a sufficiently large list, loops code will be an order of
magnitude faster.

Let's try one popular Lisp example, procedure flat? (lat?): Written
in standard Logo
     to flat? :lst
     if empty? :lst [op "true]
     op and word? first :lst flat? bf :lst
     end

When this predicate is applied to list `[1 2 3 ... 10000]', it will
run very fast--let's call it factor 1.

Written in loops code:
     to flat? :lst
     if empty? :lst [op "true]
     op and [word? first :lst] [flat? bf :lst]
     end

This will run (applied to the same list) with a factor of about 2.2,
much slower. However when both are applied to the list
`[[1] 2 3 ... 10000]', first (Logo) code will still run at factor 1.
Loops code will complete this in practically factor 0, much faster.
Even a judicious mix of loops `and/or', and Logo `and/or', can yield
factor 0, but be careful which input to `and' you quote, and which
one not. *Nonetheless do not mix!* There are no speed benefits in it,
just an increased margin for error.

     to flat? :lst
     if empty? :lst [op "true]
     op and word? first :lst [flat? bf :lst]
     end

This will run at factor 0, but if you reverse quoting, it will run at
2.2 .

Local Procedures
================

LOOPS uses LETREC SPECIAL FORM for providing local procedures in
Logo. This is not a complete truth, as Logo does not provide means
for creating real special forms. That is why LETREC syntax looks
almost like Lisp syntax, wrapping its input in lists, to protect it
from evaluation.

LETREC takes one input, a list of this format:

     LETREC [[[PROC-NAME1 [PROC-TEXT1]]
              [PROC-NAME2 [PROC-TEXT2]]
              [          ...          ]]
             [BODY-FORM1]
             [BODY-FORM2]
             [   ...    ]]

`PROC-NAME' is an ordinary word. `[PROC-TEXT]' is a list of lists in
the form that Logo's TEXT outputs, or that DEFINE accepts as input.
`[BODY-FORM]' is instruction list (ordinary Logo instruction line).
Block of BODY-FORMs functions as instruction lines in the body of TO
definition, except that each line must be a list. There are no
restrictions as to what can be done with BODY-FORMs, but their main
task is to call procedures defined as `PROC-NAME's.

LETREC outputs if one of BODY-FORMs outputs.

All procedures defined this way are local to LETREC, and not to the
context from where LETREC was invoked. That is, there is no way to
invoke PROC-NAME outside of LETREC. Order in which procedures are
defined is NOT important. BODY-FORMs can call all PROC-NAMEs, and all
procedures can call each other from their PROC-TEXTs without
restrictions.

Here is one example, that illustrates the statement above. A simple
quick-sort, written with `letrec':

     to quick.sort :lst
     op letrec [[
        [sort [[lst]
               [if or [emptyp :lst] [emptyp bf :lst] [op :lst]]
               [op (split first :lst [] [] bf :lst)]]]
        [split [[pivot left right list]
                [if emptyp :list [op (se (sort :left) :pivot (sort :right))]]
                [if lessp first :list :pivot
                    [op (split :pivot fput first :list :left :right bf :list)]]
                [op (split :pivot :left fput first :list :right bf :list)]]]]
                [op (sort :lst)]]
     end

The two local procedures `sort' and `split', are mutually recursive,
and are calling each other.

Reverting back to the performance; this code is considerably slower
(`letrec' is relatively slow) than code written in standard Logo,
using two global procedures:

     to quick.sort :lst
     if emptyp :lst [op []]
     if emptyp bf :lst [op :lst]
     op split first :lst [] [] bf :lst
     end

     to split :pivot :left :right :list
     if emptyp :list [op (se quick.sort :left :pivot quick.sort :right)]
     if lessp first :list :pivot [
        op split :pivot fput first :list :left :right bf :list]
     op split :pivot :left fput first :list :right bf :list
     end

If we recode loops example using `cond', performance will improve,
but it can not match native Logo code:

     to quick.sort :lst
     op letrec [[
        [sort [[lst]
               [condc [
                  [[or [emptyp :lst] [emptyp bf :lst]] [op :lst]]
                  [else [op (split first :lst [] [] bf :lst)]]]]]]
        [split [[pivot left right list]
                [condc [
                   [[emptyp :list] [op (se (sort :left) :pivot (sort :right))]]
                   [[lessp first :list :pivot]
                    [op (split :pivot fput first :list :left :right bf :list)]]
                   [else [op (split :pivot
                             :left fput first :list :right bf :list)]]]]]]]
                [op (sort :lst)]]
     end

Names in LETREC do not follow Logo, but Scheme rules. You can not use
the same word for variable and procedure name. That is if you had a
local (or global) variable SOME-VAR defined before LETREC, you can
not use the name SOME-VAR for PROC-NAME. Actually, you can, but the
variable will be shadowed during the life-span of LETREC. Likewise,
it is an error to use PROC-NAME as a name of a local variable, later
in the body of LETREC. However you can use the name of a previously
defined global procedure as a name for PROC-NAME, but that is not
advisable. If you do that, you should be very careful not to place a
call to global procedure in parentheses. All that I have said here
can be summed up with this: PROC-NAMEs are actually not real
procedures, but local variables.

The only special syntax rule when calling PROC-NAME within BODY-FORMs
or PROC-TEXT is that call MUST be enclosed in parenthesis:

     (PROC-NAME :input1 :input2 ...)

The following FUNNY bit of code shows that tokenization is not an
issue. It also shows that using global procedure names for PROC-NAMEs
is acceptable, as long as you observe the rules from previous
paragraph--do not parenthesize global names.

     to tokenize.test :n
     letrec [[
        [random [[]
                 [localmake "random.num sum random :n 1]
                 [pr :random.num]
                 [op :random.num]]]]
             [(pr "|result = | ((3*((random)+(random)))/(random)+(random)))]]
     end

Here is one short example of programming style that uses local
procedures. Whether this style is elegant, or not is irrelevant here.
What is important is that apart from LETREC, the whole construction
relies heavily on AND and OR--both for decision making, and result
returning. Standard Logo can not function this way.

8 queens
     ;; Start queens with queens 8, if you want only the first result
     ;; To get all results use (queens 8 "false), and be prepared for
     ;; a long wait. (Maybe you should try 6, or 4 first?)
     to queens :number [:one.only "true] 1
     ;; interpreter/compiler mark
     pr [started with letrec]
     (foreach letrec [[

        ;; Define local procedure makeboard
        [makeboard [[n]
                    [localmake "row cascade :n [[x] [op lput # :x]] []]
                    [op cascade :n [[x] [op fput :row :x]] []]]]

        ;; Define local procedure shade
        [shade [[queen rest.board [offset 1] 2]
                [if emptyp :rest.board [op []]]
                [op (fput (filter [[x] [op not (or :x = (:queen - :offset)
                                                   :x = :queen
                                                   :x = (:queen + :offset))]]
                                  first :rest.board)
                          (shade :queen bf :rest.board :offset + 1))]]]

        ;; Define local procedure put.queen
        [put.queen [[board result]

                    ;; When the board is empty, we're done
                    [if emptyp :board [push "stack :result op :one.only]]
                    [localmake "row first :board]
                    [localmake "queen and [not emptyp :row] [first :row]]

                    ;; If queen not placed -- backtrack right away
                    [op (and [:queen]
                             [or

                                ;; Go ahead -- try to place next queen
                                [(put.queen (shade :queen bf :board)
                                            lput :queen :result)]

                                ;; Nowhere to put next queen -- try to move
                                ;; this queen to next available square
                                [(put.queen fput bf :row bf :board
                                            :result)]])]]]]

                      ;; Start of letrec body forms
                      ;; interpreter/compiler mark
                      [pr [now running queens]]
                      [localmake "stack []]
                      ;; Run program and discard the output (true/false)
                      [ignore (put.queen (makeboard :number) [])]
                      ;; Output positions -- letrec ends here
                      [op reverse :stack]]
              ;; Template for foreach
              [[x] [(show "position # :x)]])
     end

You will have noticed that I use both styles for `and/or' here. `or'
in SHADE is standard Logo `or'. All `and/or's in PUT.QUEEN are LOOPS.
What kind of `or' is used in SHADE is irrelevant--however PUT.QUEEN
will not function if standard style is used.

Control Structures
==================

Use `cond', `condc', and `case' wherever possible. They are
implemented very efficiently, and at the worst will match standard
Logo code (`condc'). `Cond', and `case' will be marginally slower
than hand written code, but only if you remember to use Logo's native
`if.tf', and `ifelse.tf'. If you use loops `if', and `ifelse' for
hand written code, `cond', and `case' will be faster.

cond
----

COND takes one input, a list of this format:

     COND [
        [[CONDITIONAL1] [TRUE1a] [TRUE1b] ...]
        [[CONDITIONAL2] [TRUE2a]  ...        ]
        [    ...                      ]
        [else    [CATCH-ALL] ...      ]]

Briefly, COND works like nested IFELSEs, but with some refinements.
Each clause in COND should consist of two parts. First is CONDITIONAL
expression. If CONDITIONAL expression evaluates to true, the
corresponding TRUE forms are executed. Each clause can have more than
one TRUE form, and they look (but do not function) like body forms in
LETREC, which means that they are NOT Logo instruction lines. For
run-time efficiency reasons, compiler appends all TRUE forms
(belonging to the same CONDITIONAL) into one Logo instruction line.
Each TRUE form MUST be a list. CONDITIONAL expression can be a word,
but only one word.

COND will try all CONDITIONAL expressions until it finds one that
returns true value. Then it will execute its TRUE forms and return.
If all CONDITIONAL expressions evaluate to FALSE, else part is
executed. (ELSE is just a procedure returning TRUE.) CATCH-ALL forms
are just another name for TRUE forms, and the same rules apply. If
you omit ELSE clause, then word `#unspecified' is returned.

COND ALWAYS returns a value, therefore if you do not need this value,
you should pipe the output into `IGNORE', as in
`(... ignore cond [...] ...)'. Value returned by COND is either the
output of one of TRUE forms, or word `#unspecified', if your TRUE
forms are not returning any values. It stands to reason that all of
your TRUE forms should output, or none.

This gets somewhat more complicated, as COND can be OPERATION, or
COMMAND. When you want to use `cond' as operation (and this is
ALWAYS), you should *not* use `op/output' command in your TRUE forms.
If you adhere to this rule, `cond' will return a value that can be
used in the calling procedure (e.g.:
`make "var cond [...] ... more-code'). Likewise, it can be used as a
result of the calling procedure, (e.g.: `op cond [...] end'), or it
can be ignored (as mentioned above). To use `cond' as a command
(command in the same sense as the Logo command `output'), use `op' in
all TRUE forms, but then `cond' will output from the calling
procedure. It is an error to use `output', or `make "var' before
`cond' in this case. However, as I said before, I do not recommend
using `cond' as a command, as this would invalidate the next
paragraph.

Exceptionally, any COND clause can consist only of CONDITIONAL
expression without TRUE form. In that case, if CONDITIONAL evaluates
to any value other than FALSE, that value is returned by COND, or if
FALSE, COND moves on to the next clause. Note that COND internally
uses Logo IFELSE.TF for checking CONDITIONAL expressions, as long as
CONDITIONAL is followed by at least one TRUE form. If there are no
TRUE forms LOOPS IF is used, as in that case it MUST be able to
return values other than the word TRUE.

One simple COND example:

     cond 1

     to cond.memberp :element :list
     op cond [
        [[emptyp :list] ["false]]
        [[equalp first :list :element]]
        [else [cond.memberp :element bf :list]]]
     end

Note that `cond' is here used as an OPERATION, and does need `output'
before it. Actually to write COND.MEMBERP procedure with explicit
`op' in TRUE forms, would be an error.

     cond 2

     to cond.memberp :element :list
     cond [
        [[emptyp :list] [op "false]]
        [[equalp first :list :element]]
        [else [op cond.memberp :element bf :list]]]
     end

This is what happens when you run this with:
     pr cond.memberp 3 [1 2 3 4]
     You don't say what to do with true  in cond.memberp

condc
-----

`Condc' is a *command* version of `cond'. It was added because it is
slightly more efficient than `cond', (uses `if.tf', rather than
nested `ifelse.tf' internally. It must be used with explicitly
written `op' in TRUE forms. Using `op' before `condc' is an error
(equal to writing `op op something'. This is the correct code for
cond.memberp example, using `condc':

     to condc.memberp :element :list
     condc [
        [[emptyp :list] [op "false]]
        [[equalp first :list :element]]
        [else [op condc.memberp :element bf :list]]]
     end

By the way; `condc' stands for cond-command. Both cond.memberp and
condc.memberp examples here are coded without TRUE form in the second
COND clause `[[equalp first :list :element]]' as an illustration
only. Coding this clause with
`[[equalp first :list :element] [op "true]]' would be more efficient.

case
----

CASE takes one input, a list of this format:

     CASE [
        [TARGET-EXPR]
        [[KEYS1] [TRUE1a] [TRUE1b] ...]
        [[KEYS2] [TRUE2a]  ...        ]
        [    ...                      ]
        [else    [CATCH-ALL] ...      ]]

CASE works almost like COND with some minor differences. First clause
in CASE (TARGET-EXPR) is Logo expression that must return a value,
which I will call the KEY. This KEY is then checked (with MEMBERP)
against all KEYS. KEYS1, KEYS2... are either lists of values (sets)
or words. Bear in mind that each of the KEYS can, if word, be one
word only. When one expression MEMBERP KEY KEYS returns "true, than
its TRUE forms are executed, and their result is returned by CASE.

If all KEYS expressions evaluate to FALSE, else part is executed.
CATCH-ALL forms are just another name for TRUE forms, and same rules
apply. If you omit ELSE clause, then word `#unspecified' is returned.

CASE, just like COND ALWAYS returns a value, therefore if you do not
need this value, you should pipe the output into `IGNORE', as in
`(... ignore case [...] ...)'. Value returned by CASE is either the
output of one of TRUE forms, or word #UNSPECIFIED, if your TRUE forms
are not returning any values.

This (just as with `cond') gets more complicated, as CASE can be
OPERATION, or COMMAND. When you want to use `case' as operation, you
should *not* use `op/output' command in your TRUE forms. If you
adhere to this rule, `case' will return a value that can be used in
the calling procedure (e.g.: `make "var case [...] ... more-code').
Likewise, it can be used as a result of the calling procedure, (e.g.:
`op case [...] end'), or it can be ignored (as mentioned above). To
use `case' as a command (command in the same sense as the Logo
command `output'), use `op' in all TRUE forms, but then `case' will
output from the calling procedure. It is an error to use `output', or
`make "var' before `case' in this case.

You should be careful when using word for KEYS expression. If used to
compare numbers, the result may not be what you expect. For example;
KEY is 1 and KEYS is 3210 , will return TRUE. This is OK if you
intended to check whether 1 is a member of range 3 2 1 0 , but if
what you really want is to check whether the KEY is equal to number
3210 , then use [3210] for KEYS. Likewise it is OK to use word for
KEYS if you use a single letter for KEY (KEYS= "abcd KEY= "b), but if
you use a word like "ab for KEY, then you have to use a list of words
for KEYS.

Rules for constructing TARGET-EXPR:

TARGET-EXPR can be a constant, a variable reference or a Logo
instruction line. It can even be a recursive operation (if you use
LETREC). However remember that it must return a value.

Following are legal TARGET-EXPRs:

`["a]' , `["House]' , `[123]' , `[:var.name]' ,
`[ifelse :var.name > 10 ["a] ["b]]' , `[reduce "sum :lst]' , `[[1 2
3]]' . In this last one the value of TARGET-EXPR is a list [1 2 3].
Note that word constants (unless representing numbers) *must be
quoted*, otherwise Logo will try to run them as a procedure.

There is only one restriction to remember: You can use LOCALMAKE in
TARGET-EXPR but this variable is then visible only in TARGET-EXPR
block. What this means is; any reference to that local variable in
the rest of the CASE body will yield an unbound variable error. If
you really need this, declare the local variable (with `local
"var.name') before invocation of CASE. In TARGET-EXPR, you can use
MAKE to give the `var.name' a value. This way `var.name' will be
visible throughout the CASE.

The following example explains most of what was said above:

     to case.test :x
     local "temp
     case [

        [make "temp random :x ifelse :temp > 10 [char sum 97 random 26]
                                                [:temp]]

        [[2 4 6] [pr se :temp :#target] [op "even]]
        [[1 3 5] [pr se :temp :#target] [op "od]]
        [0 [op "zero]]
        ["abcdefghijklmnopqrstuvwxyz [pr se :temp :#target] [op "letter]]
        [else [pr (se "missed :temp :#target)] [op case.test :x-1]]]
     end

Variable #TARGET is local variable available during lifespan of CASE.
It represents the real value of KEY. In the example above, `temp'
also represents the value of KEY, but not always. Try running
`pr case.test 15' repeatedly, to get the feeling of what I'm talking
about.

casec
-----

`casec' is a *command* version of `case'. It was added for a
different reason than `condc'. CONDC is slightly more efficient than
COND, but `casec' is needed to handle objects that (most of the time)
do not output any values--as in OOP with turtles. As all objects
eventually need to output something(1) `casec' is able to handle
this. It must be used with explicitly written `op' in TRUE forms, but
*only* those that will eventually output a value. All other TRUE
forms do not have to use explicit output(2). Using `op' before
`casec' is an error (equal to writing `op op something').

Here is an example with typical use of `casec':

     to casec.test :x
     casec [
        [:x]
        [[1 3 5] [pr "odd]]
        [[2 4 6] [pr "even]]
        ["abc [pr "letter]]
        [else [op "whatever]]]
     end

Note, that the first three TRUE forms do not output anything
(`op "#unspecified' will be appended by the compiler). The `else'
clause, however, must use `op', as it does output the word WHATEVER.

CASEC is a command, in the same sense the `output' is, therefore it
will always output from the calling procedure.

---------- Footnotes ----------

(1) Delegated messages always output.

(2) Explicit `output "#unspecified' will be appended by the `casec'.

Compiler
========

After introducing Lisp structures LETREC, COND, and CASE, and due to
inefficient implementation of Logo MACRO, it soon became obvious that
the compiler is a necessity. Compiler is doing the job of Lisp
Macro--expanding the macro code, and replacing LETREC, COND, CONDC,
and CASE structures with standard Logo code. When Logo interpreter
finally runs the procedures that use LETREC, COND, and CASE, it never
sees their Lisp like structure, but only standard Logo instructions.

If you observe closely what happens when queens program is run (with
`queens 8', you will notice that there is a waiting period between
`started with letrec', and `now running queens' printouts. Then comes
the long wait for positions to print out. There is nothing I can do
about waiting for Logo to find positions, but the first waiting
period is another story. During this time interpreter is not actually
running queens, but rather interpreting letrec structure into a
usable form.

If you tried to run COND example, the situation was even worse.
`cond.memberp' is a recursive procedure, and Logo interpreted COND
structure in every invocation of `cond.memberp'. That is
prohibitively expensive.

The answer to this is, of-course, High Level Structure Compiler.

Using the Compiler
------------------

     loops.compiler [ proc1 proc2 ...]
     loops.compiler "proc
     (loops.compiler)

`loops.compiler' takes one input, a list of procedure names, or one
procedure name. If you want to compile the whole workspace, call it
without inputs:

To try it out do:
     loops.compiler "queens
Logo should respond with:
     compiling queens ... OK
     compilation completed

Now run queens again. Both STARTED WITH LETREC, and NOW RUNNING
QUEENS should print out without waiting this time. Compiled
`cond.memberp' example should likewise equal native Logo performance.

`loops.compiler' can handle very complex structures, so don't be
afraid to experiment--with nested letrec, for instance. You can
define letrec within another letrec definition, or within cond, or
case, or vice-versa. Same scope rules apply--you can NOT call inner
PROC-NAMEs from outer. Pay attention, however, to syntax rules
mentioned before (local procedure calls MUST be placed in
parenthesis).

One simple TORTURE test for compiler is converting ordinary recursive
`insert.sort' to a non-recursive version, using `cond' inside
`letrec', inside lambda, as a first input to `reduce'. In case you
wonder why this `lput (list last :lst) bl :lst' as a second input to
reduce (instead of just using `:lst') -- this is to prevent `reduce'
(actually `insert') to `butfirst' through last number in :LST as if
it were a word.

     to insert.sort :lst
     op (reduce [[e lst]
                 [op letrec [[
                    [insert [[lst]
                             [condc [
                                [[emptyp :lst] [op (list :e)]]
                                [[:e < first :lst] [op fput :e :lst]]
                                [else [op fput first :lst
                                          (insert bf :lst)]]]]]]]
                             [op (insert :lst)]]]]
                lput (list last :lst) bl :lst)
     end

There is another way to start the compiler:
     ws.compile

This is meant to be used from LOGO-MODE with `Compile Workspace'
menu, but can also be used manually. `ws.compile' is a procedure that
runs a filter on all Logo procedures in workspace and hands down to
the compiler only those that do NOT end in WHATEVER`.class' .
Procedures ending in `.class' are LOOPS classes, and are NOT supposed
to be compiled. The trouble is that most of them contain LOOPS
control structures (CASE), and loops.compiler would compile them
otherwise.

In case you want to use the compiler from a program, and do not want
to see the compiler progress printout, set the following variable to
`false':

     make ".verbose "false
     localmake ".verbose "false

The global variable `.verbose' is set to `true' on LOOPS startup. The
second version (with `localmake' is preferable, to using `make',
because you will have used it from one of your procedures before
calling the compiler, and therefore only shadowing the global
variable `.verbose'. There will be no need to restore it later.

Extending the Compiler
----------------------

Compiler is designed to be easily extended. So far, it knows that it
has to compile LETREC, COND, CONDC and CASE. Note the difference HAS
TO, as opposed to KNOWS HOW TO. The actual compilation (better word
would be CODE EXPANSION) is done by the Logo library procedure
`macroexpand', using transformer procedures for LETREC CASE and COND.
Compiler capabilities (knowledge what to compile) is stored in the
PROPERTY `compiler.cap', which is loaded on LOOPS startup.

Capabilities have this format:
     property = name of High Level Structure
     value    = "false

and their current state is:
     pprop "compiler.cap "letrec "false
     pprop "compiler.cap "cond "false
     pprop "compiler.cap "case "false
     pprop "compiler.cap "condc "false

You can add your own compiler capabilities, providing you follow
these rules: High level procedure (structure) you define must have
one input only--a list. And, of-course it must be a macro, otherwise
there is nothing for compiler to do. After you have defined (and
tested) your macro transformer procedure, add your pprop line to
lines above. That is all.

Writing macro transformer is rather difficult (at least the kind that
would require `loops.compiler', and giving instructions on how to do
this, is certainly beyond the scope of this manual. The best advice I
can give is: read COMPUTER_SCIENCE_LOGO_STYLE, VOLUME_1_2_3 BY BRIAN
HARVEY, and then take a look at LETREC, COND and CASE transformer
code in `.loops'.

Saving Compiled Procedures
--------------------------

If you want to save compiled versions of procedures do:

     save.compiled "filename.

This will save ALL COMPILED procedures that exist in the Logo
workspace at that moment. It will NOT save procedures written in
standard Logo. The idea is that you can develop a program with a mix
of LOGO and LOOPS procedures, compile LOOPS procedures, and save them
to a file. Then comment out their source in the original source file,
copy compiled versions from the file you saved them to, and paste
them into the original source file. This way, when you load the
source next time, only the compiled versions get actually loaded. I
do recommend that you keep the source (commented out), just in case
you want to change something later. Changing the compiled code
(although that is a valid Logo code), can prove to be difficult. This
is exactly what .loops file looks like; a mix of standard Logo
procedures, LOOPS procedures (commented out), and compiled LOOPS
procedures at the end.

Restoring Standard Logo
=======================

This has practical value only when used from EMACS LOGO-MODE. You can
use it to change from standard Logo to LOOPS and vice versa quickly
(with the click of the mouse), but it can also be used manually:

     restore.standard       => Standard Berkeley Logo Restored.

This will erase all LOOPS variables and properties (including object
static storage), erase all additional LOOPS procedures and
structures, and restore all standard Logo procedures that were
redefined by LOOPS. It will NOT erase additions to Logo library, as
they can be loaded on demand, and are coded in standard Logo anyway.