From: Randy Coulman
Subject: SUMMARY: Functions
Date: 
Message-ID: <1gt6hpINNn01@access.usask.ca>
I recently posted a message here asking some questions about lambda
expressions.  I promised a summary, so here it is (along with my
original post).  Thank you to all who responded.  

--- Original message:
I'm currently building an application using Allegro CL 4.1 and CLIM 1.1 on 
a Sparc 1+.  One piece of functionality I need to include in my application
is to allow users to specify and edit functions.  These functions would 
ideally be lambda expressions.  Later, the application would use apply or 
funcall to use the lambda expressions.  Unfortunately, the new standard says
that lambda expressions are no longer valid function arguments to funcall
and apply.  Instead, it is necessary to use #'(lambda ... instead of 
'(lambda ...  I can easily make it a requirement that these functions need
to be specified with #'.  However, this takes away the ability to later edit
the function.  

As an example, my application uses a class of object called Observers.  Each 
observer knows how to make one binary (i.e., t or nil) observation about the 
real world.  This is accomplished through the use of an observer function 
(ofun).  The application I'm building allows the user to define new
observers, so they need to be able to define the ofun's as well.  E.g.:

Defining Observer...
Name: Always
Ofun: '(lambda (c)
         (declare (ignore c))
         t)
...

If I use #'(lambda ... above, then editing would look like this:

Editing Observer...
Name: Always
Ofun: #<Interpreted Function (unnamed) @ ...>

Am I missing something completely obvious about how to get around this 
problem?  If not, can anyone suggest a reasonable work around?

The way I'm doing it now is to define these functions in a source file,
giving them names (strings) and storing them in a hash table.  If the user
wants to define new functions, they have to edit the source file before
loading the system.  This is not acceptable.

Thanks in advance for any help.

--- Timothy B. Moore replied:
Use eval. That's what funcall and apply did when you could pass lambda
expressions to them.
---
That's an obvious solution.  I don't know if I thought of it, but I have an
incredible aversion to eval, so I may have just dismissed the idea as soon
as I thought of it.

--- Eyvind Ness replied:
I think this is mentioned in the FAQ, but anyway:

If you have a lambda list, like '(lambda (c) (declare (ignore c)) t),
and want to funcall or apply it, simply use:

(apply/funcall (coerce '(lambda (c) (declare (ignore c)) t) 'function) ...)

In CMU CL (the only Lisp I have that is strictly CLtL2 compliant), I
get:

   magica(bash)$ lisp
   ;; Loading "/nfs/lilleulv/users/eyvind/init.lisp".
   ;;; Sun 13-Dec-92  2:36:26 PM
   CMU Common Lisp 16e, running on magica
   Send bug reports and questions to your local CMU CL maintainer, or to
   ··········@cs.cmu.edu.
   Loaded subsystems:
       Python 1.0, target SPARCstation/Sun 4
       CLOS based on PCL version:  March 92 PCL (2a)
   * (funcall '(lambda (c) (declare (ignore c)) t) nil)

   Type-error in "DEFUN FUNCALL":
      (LAMBDA (C) (DECLARE (IGNORE C)) T) is not of type (OR FUNCTION SYMBOL)

   Restarts:
     0: [ABORT] Return to Top-Level.

   Debug  (type H for help)

   ("DEFUN FUNCALL" (LAMBDA (C) (DECLARE #) T) 3670168 1)[:OPTIONAL]
   0] q

   * (funcall (coerce '(lambda (c) (declare (ignore c)) t) 'function) nil)

   T
   * 

as expected.
---

I like this idea.  It is probably the way I'll go.  I never even thought of
coerce, but it makes sense.

--- Barry Margolin replied:
In dpANS CL, you can convert a lambda expression to a function with (COERCE
<lambda-exp> 'FUNCTION).  You can also use (COMPILE NIL <lambda-exp>).

In CLtL1 CL you can get the equivalent of the COERCE with

(eval `#',<lambda-exp>)

In order for you to allow users to edit the functions later, you can
remember the original lambda expressions in a table keyed off the function.
---

As above, I like this idea best.

--- ····@ISI.EDU replied:
The problem here is that you seem to think that the function to be applied
must be the SAME as its specification.  Why not read in the specifications
as lambda expressions, but then process them a little more (in addition to
what read does) to get functions?  You certainly have to keep the source
form (the lambda expression read as a list, or the characters from which 
read produced the list) in order to allow the user to edit it.  However,
after you read it you can make a functional version for use by apply.
There are two ways to do this.  One is compile.  The other is coerce (to
type function).
---

I did realize that I would have to do something with the lambda expression
that I read in.  The problem was that I didn't know what that something
would be.  Again, I'll likely use the coerce solution.

--- John (··········@aspen.CS.Berkeley.EDU replied:
Don't use funcall, use eval instead!
I assume you aren't trying to export local variables and so just
say: (eval `(,result ,argument)) where "argument" is the value
you want passed in as "c", and "result" is the list "(lambda (c) ..."
returned after the editing session.

John
---

As above, I have this aversion to eval.  But this option would do the trick
as well.

--- Bruce Krulwich replied:
Your approach strikes me as dangerous.  What about name overlaps?  What about
the user doing something that would kill the system?

If it were me, I would probably write my own small interpreter for the
functions the user is writing.  Actually, there are plenty of small rule
interpreters around -- all you'd have to add were whatever looping constructs
or other high-level constructs you wanted.

Once you have a small vocabulary for rules the user is defining, you could in
fact make a small compiler for them, that decomposes the rules the user enters
and constructs actual closures using LAMBDAs.  The value of doing this depends
on how critical speed is and how rich your rule vocabulary is.

I hope this was clear and helps.
---

This was a very interesting reply.  I hadn't thought a lot about these kind
of issues.  This is a research system that would only be used by knowledge
engineers, so I think I can assume that there won't be any malicious
intent.  If we decide to distribute this system a little more widely, then
I'll have to reconsider these issues.

There are some problems here, though.  In general, any operations that will
be performed in these functions will be (no-side-effect) operations on the
argument to the function.  However, in some cases, there are side effects
that would be desired.  For example, one system we are building with this
environment uses these observers to recognize primitive Lisp things.  There
would have to be observers that can recognize the name of a defined
function and its arguments.  However, there also needs to be observers that
can recognize a recursive call to this function and other calls using the
function's arguments.  In this case, the observers which originally
recognize the function name and argument list must somehow store this
information for the other observers to access.

This application is a knowledge-engineering toolkit, and we intend it to be
extensible/customizable for different application domains.  Thus, we don't
want to put any a priori constraints on what these observers can and cannot
do.  For example, in the Lisp domain again, the observer may want to
recognize recursion by tracing the execution of the function to see if it
calls itself somewhere.

This response did make me think about these sort of issues, however, and I
appreciate that.

--- Richard Lynch replied:

I think if you keep the lambda list for editing, and call it with:

(funcall (function <lambda-list>) <args?>)
          ^^^^^^^^
this should work.
---

I thought of this as well, but function doesn't evaluate its argument.

Thanks again for all of the replies.  I haven't had a chance to implement
the suggestions contained here, but will do so in the near future.

Randy

-- 
Randy A. Coulman                |       ARIES Laboratory
Research Assistant              |       Department of Computational Science
                                |       University of Saskatchewan
·······@cs.Usask.ca             |       Saskatoon, SK   S7N 0W0