What can you do in LISP that you can't do in Python

Samuel A. Falvo II kc5tja at dolphin.openprojects.net
Tue May 15 11:58:47 EDT 2001


On 14 May 2001 23:47:54 GMT, Thomas Bellman wrote:
>In the examples above, the new control structures only do extra
>things before and after the code block they control, but one
>could think of examples where things should be done in the middle
>of the controlled code.  That is much easier in LISP than in any
>other language I have seen.

Forth has very similar abilities, and almost never manages to use macros.
How is this possible?

The first secret to Forth is that it's a concatenative language -- that
means you can take two simpler programs and stick them together, one after
another, to create a more intricate program.  The second secret to Forth is
that you can extend the functionality of the compiler itself in terms of the
language.  LISP doesn't have this ability -- the closest it comes to it is
macros.  For example, we have two words defined (in Forth, things between
parentheses are comments):

: +  ( n1 n2 -- n1+n2 ) ...code to add two numbers here... ;
: *  ( n1 n2 -- n1*n2 ) ...code to multiply two numbers here... ;

Each word definition above defines a single program.  We can create a more
powerful program in two different ways.  The first way is to define a new
word:

: *+  ( n1 n2 n3 -- n1+[n2*n3] )  * + ;

Note that we just "stick" * and + right next to each other.  The alternative
is to extend the compiler itself (note: it's important to note that we're
*NOT* defining a macro here, although the implementation I've chosen yields
similar results):

: *+  POSTPONE * POSTPONE + ; IMMEDIATE

Here, we've created a new compiler keyword (sort of) that itself compiles
the * and + concatenated with each other.

And we can do this infinitely if we wanted to.  Let's say that you're really
starting to inline a lot of code sequences, and you don't want to use
POSTPONE in front of every other word.  We can extend the compiler using
compiler extended words as well:

( This code may not strictly work, but I wrote it in under a minute. )
( I could write a more correct and working version if people wish.   )
( It's enough so that you can get the idea, though.                  )

( NOTE: POSTPONE: is an example of a compiler extension that isn't   )
( strictly a macro.                                                  )

( NOTE 2: This code would be much cleaner using compiler-specific    )
( functionality.  I tried to keep it as ANSI compliant as I could.   )

: INLINE:
  32 WORD   ( Get next word in the input stream.  Exception if failure )
  BEGIN DUP COUNT S" ;INLINE" STRING-COMPARE WHILE
    FIND IF
      ( What follows is generally Forth compiler specific, due to the )
      ( different ways a Forth program is compiled: native code,      )
      ( direct threaded, indirect threaded, subroutine threaded,      )
      ( and token threaded.  Unfortunately, the ANSI wordsets do not  )
      ( provide enough basic functionality to support all these       )
      ( different compilation techniques.  I'm going to assume        )
      ( direct threaded for this example, since it's the easiest to   )
      ( understand, and most often used.                              )
      
      ['] LITERAL , ,	( Pushes word address onto the stack when run )
      ['] , ,		( Compiles it into the dictionary when run )
    ELSE
      -13 THROW  ( Can't find the word; abort )
    THEN
    32 WORD
  AGAIN
  DROP
; IMMEDIATE

So this code:

: *+ INLINE: * + ;INLINE ; IMMEDIATE

is compiled literally as follows:

: *+ ['] * , ['] + , ; IMMEDIATE

which is what we wanted to do in the first place (POSTPONE takes care of the
black magic of which compilation technique the compiler uses; unfortunately,
it accepts its input from the next word in the input stream -- that's the
word we've already consumed with "32 WORD" above.  Therefore, we cannot use
POSTPONE in our INLINE: definition without some really tricky input buffer
manipulation, which is every bit as Forth compiler specific as what we're
doing.  :-) ).

At any rate, I'm rambling.  The point is, Lisp isn't alone in the category
of languages that supports operations to be performed on program code.  The
philosophy between Lisp and Forth is quite different, but the end result is
very much the same.  Much of Forth's "basic syntax" is implemented using
IMMEDIATE words (so named for historical reasons; words marked as such are
executed "immediately", regardless of whether they're inside or outside a
word definition).  Astonishingly little of Forth's basic vocabulary is
implemented using native assembly.

BTW: In general, it is very rare to see Forth is better than Lisp type
discussions.  Forth and Lisp programmers respect each language very much,
especially considering the similarities between the two languages.  I do
wish Forth had more dynamic memory management, but as it is, it's at least
as good as C, which is "good enough" for most applications.

-- 
KC5TJA/6, DM13, QRP-L #1447
Samuel A. Falvo II
Oceanside, CA



More information about the Python-list mailing list