From: Paul Tarvydas
Subject: Compile vs. Xanalys
Date: 
Message-ID: <P3aI7.181317$YL3.54624062@news3.rdc1.on.home.com>
I need a second set of eyes to tell me what stupid thing I've done.

I've been using a "trick" for months, apparently successfully.  I then
cut/copy/pasted the trick into a fresh copy of the code and it appears to
fail.  The code snippet below is a reduced form of the trick:

(compile 'xx '(lambda ()
                (defun yy () 1)
                (defun zz () (yy))
                (vector 'yy 'zz)))

This snippet, when executed in the Listener give the error "function
undefined ... YY which is referenced by XX".  I don't remember seeing this
error in the "old" version of the code.

In case you wish to suggest a better way of doing what I intend, I'll
explain what I intend:

I've got a picture (e.g. a flowchart) which is annotated with lisp code
snippets.  I want to collect up all of the snippets into a self-consistent
lump and then, I want to instantiate the lump numerous times.  Each lump
should execute the same code, but with a different set of "self" variables.
The code snippets should look like normal lisp, i.e. I don't want to have
explicit references to some sort of "self" structure.

In the example above, the compile lambda would have been created by pasting
two snippets together.  The first snippet was "(defun yy () 1)" and the
second snippet was the call (yy) (which is wrapped automatically into its
own defun).  [My explanation appears asymmetrical - the actual generated
code consists of a "global" section (defvars and defuns) prepended onto a
bunch of expression snippets, where each snippet is wrapped into a defun
(using a gensym'ed name)].

I decided to wrap the code snippets into a closure and return a vector of
pointers into the closure.  So, in the example above, I would instantiate
the lump by calling the result of the compile.  This would return a vector
of functions that I would then use to index into the closured-functions
belonging to the lump.  For example (funcall (elt lump 1)), would call zz
which would then call yy.

Thanks in advance for any advice...

pt
·········@tscontrols.com

From: Erik Haugan
Subject: Re: Compile vs. Xanalys
Date: 
Message-ID: <87lmha67iz.fsf@kometknut.neitileu.no>
* "Paul Tarvydas" <·········@tscontrols.com>
> I've been using a "trick" for months, apparently successfully.  I then
> cut/copy/pasted the trick into a fresh copy of the code and it appears to
> fail.  The code snippet below is a reduced form of the trick:
> 
> (compile 'xx '(lambda ()
>                 (defun yy () 1)
>                 (defun zz () (yy))
>                 (vector 'yy 'zz)))
> 
> This snippet, when executed in the Listener give the error "function
> undefined ... YY which is referenced by XX".  I don't remember seeing this
> error in the "old" version of the code.

It's not an error, it's a warning.  This is as it should be, because the
functions yy and zz are not defined at compile time.  They don't become
defined until you call the function xx.  defun forms are evaluated at
compile time only if they are top-level forms.

It's not the code that has changed, it's the environment, in your old
environment yy obviously was already defined when you evaluated the compile
form.

> In case you wish to suggest a better way of doing what I intend, I'll
> explain what I intend:

I didn't really get what you're trying to do, so the following are just
general hints.  Unless you really must be able to compile new functions at
run time, you probably should consider using macros.  I also think you
should consider if you really want your code to define global functions.
Maybe dealing with function objects directly or making your own
symbol->function table would be a good idea.

Erik
From: Kent M Pitman
Subject: Re: Compile vs. Xanalys
Date: 
Message-ID: <sfwsnbin24i.fsf@shell01.TheWorld.com>
"Paul Tarvydas" <·········@tscontrols.com> writes:

> I need a second set of eyes to tell me what stupid thing I've done.
> 
> I've been using a "trick" for months, apparently successfully.  I then
> cut/copy/pasted the trick into a fresh copy of the code and it appears to
> fail.  The code snippet below is a reduced form of the trick:
> 
> (compile 'xx '(lambda ()
>                 (defun yy () 1)
>                 (defun zz () (yy))
>                 (vector 'yy 'zz)))

You're returning a vector containing the symbols, not the function defs.
That seems odd.
 
> This snippet, when executed in the Listener give the error "function
> undefined ... YY which is referenced by XX".  I don't remember seeing this
> error in the "old" version of the code.

This is a correct error message.  The compiler does not scan for definitions
within code in order to do "undefined" processing.  To understand why,
consider:  
 (compile 'xx '(lambda (z)
                 (if z (defun yy () 1))
                 (defun zz () (yy))
                 (vector 'yy 'zz)))
The compiler isn't obliged to keep track within code of which definitions
will, might, etc. get defined.

Why don't you just do

 (compile nil '(lambda ()
                 (labels ((yy () 1) 
                          (zz () (yy)))
                   (vector #'yy #'zz))))

This would even work without the gensyms.
 
> In case you wish to suggest a better way of doing what I intend, I'll
> explain what I intend:
> 
> I've got a picture (e.g. a flowchart) which is annotated with lisp code
> snippets.  I want to collect up all of the snippets into a self-consistent
> lump and then, I want to instantiate the lump numerous times.  Each lump
> should execute the same code, but with a different set of "self" variables.
> The code snippets should look like normal lisp, i.e. I don't want to have
> explicit references to some sort of "self" structure.
> 
> In the example above, the compile lambda would have been created by pasting
> two snippets together.  The first snippet was "(defun yy () 1)" and the
> second snippet was the call (yy) (which is wrapped automatically into its
> own defun).  [My explanation appears asymmetrical - the actual generated
> code consists of a "global" section (defvars and defuns) prepended onto a
> bunch of expression snippets, where each snippet is wrapped into a defun
> (using a gensym'ed name)].

Are you REALLY creating new "forms" at runtime? I somehow bet that you could
get rid of all the calls toe eval/compile if you worked at it, by 
composing functional blocks instead of algebraic ones... ESPECIALLY if you're
in the realm of gensyms, which give up the only two reasons I can think of
for using symbols: human visual readability and the ability to type in
the names of expressions during debugging.
 
> I decided to wrap the code snippets into a closure and return a vector of
> pointers into the closure.  So, in the example above, I would instantiate
> the lump by calling the result of the compile.  This would return a vector
> of functions that I would then use to index into the closured-functions
> belonging to the lump.  For example (funcall (elt lump 1)), would call zz
> which would then call yy.

Or why not do:

 (setq lump17 (compile nil '(lambda ()
                              (labels ((yy (self)
                                         (declare (ignorable self))
                                         #'(lambda () 1))
                                       (zz (self)
                                         (declare (ignorable self))
                                         #'(lambda () (yy))))
                                (vector #'yy #'zz)))))
 (defun close-lump (lump self)
   (dotimes (i (length lump))
     (setf (aref lump i) (funcall lump self))))

I have a feeling you really want CLOS and instances, which is what most
people want when they start wanting a manipulable or lazily initializable
lexical environment, but I'll let you figure out that part yourself.
From: Paul Tarvydas
Subject: Re: Compile vs. Xanalys
Date: 
Message-ID: <GAwI7.186334$YL3.56760254@news3.rdc1.on.home.com>
Erik, Kent, Thanks for the replies!

Just to close the loop:  yes, I want to construct new code objects at run
time using whatever method is "easiest".

If you want to read on, I'll describe my goals a bit further and will gladly
accept advice...

We're building a Windows-based IDE for embedded system development that uses
a proprietary interpreted language.  The applications are object-based and
it would be nice if the development IDE were fairly interactive.

I'm using Lisp to implement a large portion of the IDE.  We will simulate
the final applications by running the interpreted code under Windows.  It
would be nice to be able to "break out" to Lisp where necessary (esp. to
implement fake I/O drivers, etc.).

Using the IDE, a programmer draws a variety of pictures of his/her code
(e.g. state machine drawings), annotates parts of the pictures using code
(i.e. the interpreted language).  When the "compile" button is pushed, the
IDE compiles the pictures into data structures with bytecodes at the nodes.

Under Windows, in the Lisp environment, it is also easy to annotate the
pictures using Lisp code, then compile the pictures to data structures with
callable lisp at the nodes.  I could then run the resulting mixture of
interpreted-code and Lisp under Windows.  To maintain the desired semantics,
I should wrap the contents of each drawing into separate "environments"
(pictures cannot leak data / calls beyond their edges, except through
explicitly defined ports).  More than one instance of a picture can be
created at run time.

The code belonging to a single picture consists of two types of things - a
single lump of "global" variables and functions (global throughout the same
picture, but not exported to any other picture) plus a set of code snippets
that annotate states and transitions (i.e. statements and expressions that
may or may not refer to things in the "global" chunk).

The code is entered as text and stored in the IDE as text.  When the COMPILE
button is pushed, the text plus data structures plus compiler-generated
control flow statements are concatenated into a single entity and made to be
instantiable and executable.  (In the worst case, I could emit the text, in
the correct order, into a file, then compile-and-load the file, but I think
that I can do better / more-interactively than this).

Thanks again
pt