[comp.lang.lisp] Lisp 1.5 functional values as objects

dave@hpgnd.HP.COM (Dave PENKLER) (10/09/90)

A recent discussion on whether CL functions are first class objects brought up
a point on the run time creation of functions. This is somthing I missed in CL
and have consequently remained stranded in the ancient world of Ye Old Lisp.

Ye old lisp's abilty to handle functional values can be interpreted as a sort
of support for object oriented development. The beauty of it is its extreme
simplicity in the prinicple of operation permitting at the same time 
arbitrarily complex hierachies and collections of objects. For free you have
the ability to modify methods in a given instance, all instances of a class
or future instances. This is great for modelling and simulations where
you may need to tweak individual instances during the run to see what the
effects on the system might be. Dynamic scoping gives you what I call dynamic
inheritance where free variables/methods in the closure are resolved in the 
invoking environment and captured in the instance.

Here is an ultra simple example to give the basic idea of functional values
as objects:

To create a class you simply define a function that returns itself in a funarg -

 (defun SIMP (X Y) ; inittable instance vars X and Y
    (prog (Z SHOW) ; instance var Z and a method SHOW
	  (setq Z 'some-default) ; initial value of Z 
	  (defun SHOW () (print (list X Y Z))) ; method to print instance vars 
          (fun SIMP))) ; return self in funarg

To create an instance -

(setq INST1 (SIMP 'fred 'joe))

To apply the method SHOW to our instance -

 (send '(SHOW) INST1)  ; show its guts
(fred joe some-default)

The real work here is done by "send" 
(defun send (EXP OBJ) (apply (list 'close (car EXP) (caddr OBJ)) (cdr EXP)))


Is there any way to do this sort of thing in  newer dialects of lisp ?

DaveP

masinter@parc.xerox.com (Larry Masinter) (10/10/90)

In article <1950001@hpgnd.HP.COM> dave@hpgnd.HP.COM (Dave PENKLER) writes:
> A recent discussion on whether CL functions are first class objects brought up
> a point on the run time creation of functions. This is somthing I missed in CL
> and have consequently remained stranded in the ancient world of Ye Old Lisp.

> Ye old lisp's abilty to handle functional values can be interpreted as a sort
> of support for object oriented development. The beauty of it is its extreme
> simplicity in the prinicple of operation permitting at the same time 
> arbitrarily complex hierachies and collections of objects. For free you have
> the ability to modify methods in a given instance, all instances of a class
> or future instances. This is great for modelling and simulations where
> you may need to tweak individual instances during the run to see what the
> effects on the system might be. Dynamic scoping gives you what I call dynamic
> inheritance where free variables/methods in the closure are resolved in the 
> invoking environment and captured in the instance.

> Here is an ultra simple example to give the basic idea of functional values
> as objects:

> To create a class you simply define a function that returns itself in a funarg -

>  (defun SIMP (X Y) ; inittable instance vars X and Y
>     (prog (Z SHOW) ; instance var Z and a method SHOW
> 	  (setq Z 'some-default) ; initial value of Z 
> 	  (defun SHOW () (print (list X Y Z))) ; method to print instance vars 
>           (fun SIMP))) ; return self in funarg

> To create an instance -

> (setq INST1 (SIMP 'fred 'joe))

> To apply the method SHOW to our instance -

>  (send '(SHOW) INST1)  ; show its guts
> (fred joe some-default)

> The real work here is done by "send" 
> (defun send (EXP OBJ) (apply (list 'close (car EXP) (caddr OBJ)) (cdr EXP)))

> Is there any way to do this sort of thing in  newer dialects of lisp ?

> DaveP

The problem is that the 'close' operator was generally very expensive,
having to wind and then unwind the dynamic context (or else do dynamic
searching for bindings every time, which is even more expensive.)

While not entirely equivalent, this sort of stuff is usually done by
being more explicit about the 'search' for the appropriate method,
e.g.,

(defun simp (x y)
  (let ((z 'some-default))
     #'(lambda (method &rest args)
         (ecase method
           (show (print (list x y z)))
           (set-z (setq z (first args)))))))

(setq inst1 (simp 'fred 'joe))

;Usually to avoid interpretation, the "send" primitive separates out the 
;method from the arguments, e.g.,

(defmacro send (method object &rest args)
   `(funcall ,object ',method ,@args))

;To use

(send show inst1)              ; prints   (fred joe some-default)
(send set-z inst1 'new-z)      ; reset    z
(send show inst1)              ; prints   (fred joe new-z)


; If you want to evaluate the method, you're better off using a 
; function to get order-of-evaluation right:

(defun send1 (method object &rest args)
   (apply object method args))
(send1 'show  inst1)
(send1 'set-z inst1 'newer-z)
(send1 'show  inst1) 

; If you want to pretty it up and hide the mechanism:

(defmacro methods (&rest method-list)
  `#'(lambda (method &rest args)
       (ecase method
	 ,@(mapcar
	    #'(lambda (mproc)
		`(,(car mproc) (apply #'(lambda ,@(cdr mproc)) args)))
	    method-list)))) 

; and then you can write

(defun simp (x y)
  (let ((z 'some-default))
     (methods
        (show  ()     (print (list x y z)))
        (set-z (newz) (setq z newz)))))


This is a little bit of a simplification in that you'd want to avoid
symbol conflicts either by having 'method' and 'args' in a private
package or else using gensyms, e.g.:

(defmacro methods (&rest method-list)
  (let ((mv (gensym)) (av (gensym)))
  `#'(lambda (,mv &rest ,av)
       (ecase ,mv
	 ,@(mapcar
	    #'(lambda (mproc)
		`(,(car mproc) (apply #'(lambda ,@(cdr mproc)) ,av)))
	    method-list)))) 

--
Larry Masinter (masinter@parc.xerox.com)
Xerox Palo Alto Research Center (PARC)
3333 Coyote Hill Road; Palo Alto, CA USA 94304
Fax: (415) 494-4333

raja@copper.ucs.indiana.edu (Raja Sooriamurthi) (10/10/90)

dave@hpgnd.HP.COM (Dave PENKLER) writes:


>A recent discussion on whether CL functions are first class objects brought up
>a point on the run time creation of functions. This is somthing I missed in CL
>and have consequently remained stranded in the ancient world of Ye Old Lisp.

>Ye old lisp's abilty to handle functional values can be interpreted as a sort
>of support for object oriented development. The beauty of it is its extreme
>simplicity in the prinicple of operation permitting at the same time 
>arbitrarily complex hierachies and collections of objects. For free you have
>the ability to modify methods in a given instance, all instances of a class
>or future instances. This is great for modelling and simulations where
>you may need to tweak individual instances during the run to see what the
>effects on the system might be. Dynamic scoping gives you what I call dynamic
>inheritance where free variables/methods in the closure are resolved in the 
>invoking environment and captured in the instance.

>Here is an ultra simple example to give the basic idea of functional values
>as objects:

>To create a class you simply define a function that returns itself in a funarg -

> (defun SIMP (X Y) ; inittable instance vars X and Y
>    (prog (Z SHOW) ; instance var Z and a method SHOW
>	  (setq Z 'some-default) ; initial value of Z 
>	  (defun SHOW () (print (list X Y Z))) ; method to print instance vars 
>          (fun SIMP))) ; return self in funarg

>To create an instance -

>(setq INST1 (SIMP 'fred 'joe))

>To apply the method SHOW to our instance -

> (send '(SHOW) INST1)  ; show its guts
>(fred joe some-default)

>The real work here is done by "send" 
>(defun send (EXP OBJ) (apply (list 'close (car EXP) (caddr OBJ)) (cdr EXP)))


>Is there any way to do this sort of thing in  newer dialects of lisp ?
                                               ^^^^^^^^^^^^^^^^^^^^^^


This might be out of place in this news-group, but Scheme can handle
these sort of objects very elegantly. For instance a stack could be
defined as:

(define make-stack
  (lambda () ; if desired initial values could be passed here
    (let ([stack 'any-initial-value])
      (lambda msg  ; the function (object) that is returned
	(case (car msg)
	  [push (set! stack (cons (cadr msg) stack))]
	  [pop (set! stack (cdr stack))]
	  [top (car stack)]
	  [show stack]
	  [flush (set! stack 'any-initial-value)]
	  [else (error 'stack "unknown method name")])))))

(define send
  (lambda args
    (apply (car args) (cdr args))))

You would create stacks as:

(define s1 (make-stack))

and manipulate it as:

(send s1 'push 'apple)
(send s1 'push 'orange)
(send s1 'show)

...

In this style of creating objects, values can be shared between different
classes by means of delegation.

Springer and Friedman devote an entire chapter to this mechanism of creating
objects in their book _Scheme and the art of Programming_  (MIT press
and McGraw  Hill, 1989)

- Raja

---------------------------------------------------------------------------
Raja Sooriamurthi                              Computer Science Department
raja@copper.ucs.indiana.edu                       Indiana University
---------------------------------------------------------------------------

moore%cdr.utah.edu@cs.utah.edu (Tim Moore) (10/10/90)

In article <raja.655569550@copper> raja@copper.ucs.indiana.edu (Raja Sooriamurthi) writes:
>dave@hpgnd.HP.COM (Dave PENKLER) writes:
>
>>Is there any way to do this sort of thing in  newer dialects of lisp ?
>
>This might be out of place in this news-group, but Scheme can handle
>these sort of objects very elegantly. For instance a stack could be
>defined as:
>
>(define make-stack
>  (lambda () ; if desired initial values could be passed here
>    (let ([stack 'any-initial-value])
>      (lambda msg  ; the function (object) that is returned
>	(case (car msg)
>	  [push (set! stack (cons (cadr msg) stack))]
>	  [pop (set! stack (cdr stack))]
>	  [top (car stack)]
>	  [show stack]
>	  [flush (set! stack 'any-initial-value)]
>	  [else (error 'stack "unknown method name")])))))
>
>(define send
>  (lambda args
>    (apply (car args) (cdr args))))
>

Scheme doesn't have a monopoly on this style of programming; Common
Lisp (and any language with closures) can do the same thing:

(defun make-stack ()
  (let ((stack nil))
    #'(lambda (cookie &optional val)
	(case cookie
	  (push (push val stack))
	  (pop (pop stack))
	  (top (car stack))
	  (show stack)
	  (flush (setq stack nil))
	  (t (error "unknown method name"))))))

(defun send (object &rest args)
  (apply object args))

>In this style of creating objects, values can be shared between different
>classes by means of delegation.
>
>Springer and Friedman devote an entire chapter to this mechanism of creating
>objects in their book _Scheme and the art of Programming_  (MIT press
>and McGraw  Hill, 1989)

Also, this technique is used in Abelson and Sussman, "Structure and
Interpretation of Computer Programs", MIT press, 1985.

Incidently, I feel that Common Lisp, with the exception of call/cc,
has the same semantic power (and much more) of scheme. Any
disagreement? (Warning, religious war ahead!)
>- Raja
Tim Moore                    moore@cs.utah.edu {bellcore,hplabs}!utah-cs!moore
"Ah, youth. Ah, statute of limitations."
		-John Waters

andreasg@boulder.Colorado.EDU (Andreas Girgensohn) (10/11/90)

In article <raja.655569550@copper> raja@copper.ucs.indiana.edu (Raja Sooriamurthi) writes:
>dave@hpgnd.HP.COM (Dave PENKLER) writes:
>>[...]
>>Is there any way to do this sort of thing in  newer dialects of lisp ?
>                                               ^^^^^^^^^^^^^^^^^^^^^^
>This might be out of place in this news-group, but Scheme can handle
>these sort of objects very elegantly. For instance a stack could be
>defined as:
>
>(define make-stack
>  (lambda () ; if desired initial values could be passed here
>    (let ([stack 'any-initial-value])
>      (lambda msg  ; the function (object) that is returned
>	(case (car msg)
>	  [push (set! stack (cons (cadr msg) stack))]
>	  [pop (set! stack (cdr stack))]
>	  [top (car stack)]
>	  [show stack]
>	  [flush (set! stack 'any-initial-value)]
>	  [else (error 'stack "unknown method name")])))))
>
>(define send
>  (lambda args
>    (apply (car args) (cdr args))))
>
>You would create stacks as:
>(define s1 (make-stack))
>
>and manipulate it as:
>(send s1 'push 'apple)
>
>[...]

That example works as well in Common Lisp or any other Lisp dialect that
provides lexical closures.

(defun make-stack () ; if desired initial values could be passed here
  (let ((stack 'any-initial-value))
    #'(lambda (&rest msg)  ; the function (object) that is returned
        (case (car msg)
          (push (setq stack (cons (cadr msg) stack)))
          (pop (setq stack (cdr stack)))
          (top (car stack))
          (show stack)
          (flush (setq stack 'any-initial-value))
          (otherwise (error "unknown method name"))))))

(defun send (&rest args)
  (apply (car args) (cdr args)))

(setq s1 (make-stack))

(send s1 'push 'apple)
(send s1 'push 'orange)
(send s1 'show)

Andreas Girgensohn
andreasg@boulder.colorado.edu

jeff@aiai.ed.ac.uk (Jeff Dalton) (10/12/90)

In article <1950001@hpgnd.HP.COM> dave@hpgnd.HP.COM (Dave PENKLER) writes:

>A recent discussion on whether CL functions are first class objects
>brought up a point on the run time creation of functions. This is
>somthing I missed in CL and have consequently remained stranded in the
>ancient world of Ye Old Lisp.

From your title, it looks like the "Old Lisp" you're talking about
is Lisp 1.5.  However, none of your examples are Lisp 1.5 unless you
add some other code you have not described.  Consequently, it is
impossible to tell whether the same thing can be written in Common
Lisp (in a reasonably "direct" way, that is -- you could always write
an interpreter or compiler for it).

Anyway, it is certainly *not* the case that CL programs cannot create
functions at run-time, so you must want something more than that.

-- Jeff