noren@dinl.uucp (Charles Noren) (01/10/90)
It's been a while since I've been on the net. I can no longer access comp.lang.lisp.x from our site, I suppose it went away? I've just starting playing with XLISP v2.0, particularly the object-oriented features of it. I've created new classes with instance and class variables, and I've used the :new selector to do so and it works just fine. However, I see the :isnew selector in the documentation and I was wondering how that works compared to :new. Also, to show my ignorance, I've heard the new Common Lisp standard includes objects. How does this compare to objects in XLISP (unfortunatly I do not know "flavors" or the other object-oriented variants of LISP). Is there a good reference on Common Lisp objects that anyone would recommend? Thanks, -- Chuck Noren NET: ncar!dinl!noren US-MAIL: Martin Marietta I&CS, MS XL8058, P.O. Box 1260, Denver, CO 80201-1260 Phone: (303) 971-7930
mayer@hplabsz.HPL.HP.COM (Niels Mayer) (01/10/90)
In article <1511@dinl.mmc.UUCP> noren@dinl.UUCP (Charles Noren) writes: >It's been a while since I've been on the net. I can no longer >access comp.lang.lisp.x from our site, I suppose it went away? it's still there... >I've just starting playing with XLISP v2.0, particularly the >object-oriented features of it. I've created new classes with >instance and class variables, and I've used the :new selector >to do so and it works just fine. However, I see the :isnew >selector in the documentation and I was wondering how that works >compared to :new. When I first looked at XLISP, I too found the documentation on the object system to be a little terse. Everything becomes much clearer once you see some examples. I recently wrote up some documentation on XLISP's object system for use with WINTERP (an XLISP-based rapid prototyping environment for applications based on the OSF Motif widgets). The following excerpt from winterp/doc/winterp.doc may help (get winterp via anonymous ftp from expo.lcs.mit.edu:oldcontrib/winterp.tar.Z). In particular, your question about :ISNEW is answered in the "object initialization" section. -------------------- * Introduction to XLISP objects and Widgets. WINTERP uses XLISP's object system as its interface to the class hierarchy of widgets provided by Motif. Specifically, each Motif widget class is represented by one or more object classes in WINTERP. In order to best understand the capabilities of WINTERP's Motif interface, a brief review of the XLISP object system is in order. You may also want to consult the XLISP documentation ./winterp/doc/xLisp.doc for a more precise definition of the object system. XLISP Classes describe the type of a particular object by declaring a set of variables held in each object. These "instance variables" may only be accessed by "methods" that respond to "messages" sent to the object. Methods are defined for particular classes, and functionality of other classes may be incorporated into new classes via "inheritance". From XLISP, Motif widget classes look just like normal XLISP objects -- that means that you can easily extend the functionality of Motif widgets by adding your own methods to a particular widget class. You may also use inheritance to attach your own data structures to widgets. The result is that WINTERP provides a very clean way to interactively rapid-prototype an application, while also providing mechanisms for code structuring and reuse. The latter is necessary in evolving from prototype to a structured, maintainable, and customizable deliverable. ** Creating new objects. Create a new instance of a class by sending the message :NEW to <a_class_instance>: (SEND <a_class_instance> :NEW <parameters>) <a_class_instance> is in fact an instance of class CLASS. Class CLASS allows you to define new class instances by specifying the instance variables and parent class of a particular class. ** Declaring a class. To declare a "base class" object, that is, an object with no parent object, just send message :NEW to the object <CLASS> (SEND CLASS :NEW '(<ivar0> ... <ivarN>) ['(<cvar0> ... <cvarM>)]) '(<ivar0> ... (ivarN>) are a list of symbols. Each <ivar-i> names an instance variable of the class. '(<cvar0> ... <cvarM>)]) are an optional list of variables that are shared among all instances of that particular class. ** Defining methods. When a "message" is sent to an object, XLISP searches for a "method" to answer the message. A method is a piece of Lisp code that is executed when a particular message is sent to an object. Within the code of a method, all object instance and class variables are accessible. Furthermore, the symbol 'self' is bound to the object the message was sent to. Methods are defined by sending the message :ANSWER to <a_class_instance>: (SEND a_class_instance :ANSWER <:msg> <parameters> <code>) where <:msg> is a keyword symbol (a symbol with a ':' prefix) representing the message; <parameters> are the arguments given along with the message. See the documentation on "lambda lists" in /winterp/doc/xLisp.doc p.12 for details. <code> is a list of s-expressions which get evaluated in response to a message. The lexical environment that existed for the call to :ANSWER will be used for value and functional bindings during method evaluation. If you need to remember what the syntax is, consider the memory-helper "this class :ANSWERs to :MESSAGE..." == (send <cls> :ANSWER :MESSAGE ...) ** Inheritance So far, the object system we just described supports *encapsulation*. Encapsulation is good programming practice because it helps localize and detangle complexity. Unfortunately, encapsulation runs counter to flexibility because making flexible use of an object may require that certain groups of instance variables be accessed by different layers of new functionality. Most often, one wants to *reuse* aspects of a particular class in creating code that specializes and alters the functionality of that class. A compromise between encapsulation and flexibility is found by using *inheritance* in an object system. Inheritance is used to allow a *subclass* to specialize the functionality of it's *parent class* (aka, the *superclass*): (send Class :NEW '(<ivar0> ... <ivarN>) '(<cvar0> ... <cvarM>) <superclass>) (<ivar0> ... <ivarN>) is a list of new instance variables in the subclass; (<cvar0> ... <cvarN>) is a list of new class variables in the subclass; <superclass> is a class instance representing the parent from which the new subclass inherits variables and methods. "Inheritance" is occurring because all the instance variables of all the parent classes of the new subclass become the variables of each subclass instance. Furthermore, all methods defined on a parent class may also be used on a subclass instance. Note that while a subclass' methods can access the variables defined on the parent classes, the reverse isn't true. ** Object initialization. As mentioned earlier, new object instances are created by sending the message :NEW to a class object. Sending the message :NEW to a class automatically sends message :ISNEW to the newly created instance. By default :ISNEW on an instance is a no-op method defined on class 'Object', which is the implicit [(grand)*]parent of all instances. If you want to initialize the instance/class variables of a particular class, you must define an :ISNEW method on the class. Any parameters originally sent to the :NEW method will be passed on to the :ISNEW method and may be used to initialize an object to outside-world parameters. ** Example of using OOP features of XLISP with Motif widgets: As currently implemented, the Motif class xmListWidgetClass makes it a bit baroque to create browsers (hopefully this will change in Motif 1.1). The problem is that a "browser" is a kind of application that lends itself to object oriented techniques that are not always straightforward to support in C. One often has a collection of 'things' that one wants to display in a 'list' and perform actions on the 'thing' corresponding to the visual selection of an element in the displayed list. xmListWidgetClass will display an arrray of XmStrings in a list. When one or more elements in the list are selected, XmStrings corresponding to the selected elements are returned. Since the XmStrings you put into the list widget are not the XmStrings you get out, you must call XmStringCompare on each element of the collection of 'things' to find out which ones are selected. Presumably, you'll want to do more than just get back an XmString... normally one will want to access data structures associated with the XmString so as to perform an action dependent on those structures. This could be done with a lookup table, but there's also a better way... WINTERP allows us to subclass the Motif list widget so as to make it have the kind of functionality we want. This subclass will contain an additional instance variable 'items' which is an array of arbitrary XLISP objects to be displayed in a textual browser made from the list widget. These objects can have completely different internal representations -- the only requirement is that they follow the protocol of being able to respond to the messages :DISPLAY_STRING and :DEFAULT_ACTION. :DISPLAY_STRING returns a string representation of the object to be displayed in the browser. :DEFAULT_ACTION is the action to be performed when a list item is browsed (by double clicking on the item). The following creates the subclass <List_Browser> from superclass <XM_LIST_WIDGET_CLASS>: (setq List_Browser (send Class :NEW ;create a class inst '(items) ;new instance vars '() ;no class vars XM_LIST_WIDGET_CLASS)) ;superclass So now all instances of <List_Browser> will contain an instance variable <items> and will respond to all the messages understood by the XM_LIST_WIDGET_CLASS. We want our list browser to behave as described above, so we define an :ISNEW method to initialize instance variable <items> to the list of arbitrary objects to be displayed. <items> gets initialized to an array of objects, the list widget is created, and a XmNdefaultActionCallback is setup so that a double click will send the message :DEFAULT_ACTION to the browsed item: ;; (send List_Browser :new <items_list> <args-for-the-list-widget>) ;; <items_list> is a list of BROWSER_OBJECTs as described above. ;; <args-for-the-list-widget> -- these are the arguments that ;; will be passed on to the list widget ;; (send List_Browser :answer :isnew '(items_list &rest args) '( (let* ( (items_end_idx (length items_list)) (display_items (make-array items_end_idx))) ;; initialize the 'items' instance variable so that it ;; holds all the BROWSER_OBJECTs passed in <items_list> (setq items (make-array items_end_idx)) ;create the array (do ( ;copy elts from list to array (i 0 (1+ i)) (elts items_list (cdr elts))) ;; loop till no more elts ((null elts)) ;; loop body (setf (aref items i) (car elts)) (setf (aref display_items i) (send (car elts) :display_string)) ) ;; initialize the widget, passing in the browser items. (apply 'send-super `(:isnew ,@args :xmn_selection_policy :browse_select :xmn_items ,display_items :xmn_item_count ,items_end_idx )) ) ;; set up a callback on the list widget initialized above such ;; that a double click on the browser-item will send the ;; message :default_action to the BROWSER_OBJECT. (send-super :add_callback :xmn_default_action_callback '(callback_item_position) '((send (aref items (1- callback_item_position)) :default_action)) ) ) ) In the above code, SEND-SUPER works just like send, except that it doesn't require you to give it the object to send the message to. Instead, it implicitly assumes that it will be called from within a method, and will automatically send the message to a superclass of the object's class. The (apply 'send-super ...) form is actually calling the :ISNEW (instance initializer) method on XM_LIST_WIDGET_CLASS, which actually creates the widget via XmCreateList() or XmCreateScrolledList(). The APPLY '`' (BACKQUOTE) and '&rest args' (LAMBDA LIST) features of Lisp allow us to splice in the argument list passed to the instance of List_Browser into the function that actually creates the widget. Finally, method :add_callback is the WINTERP equivalent of XtAddCallback(). See the documentation on methods on WIDGET_CLASS for more details. The Motif List widget also defines a number of "methods" implemented as C routines such as XmListAddItem(), XmListAddItemUnselected(), XmListDeleteItem(), and XmListDeletePos(). In WINTERP, we define these as methods :ADD_ITEM, :ADD_ITEM_UNSELECTED, :DELETE_ITEM, and :DELETE_POS respectively. Since these methods modify the collection of objects represented by the list widget, we must update the internal array of objects <items> to correspond with the items displayed. We do this by intercepting those messages to the superclass of class <List_Browser> and handle them in <List_Browser> so as to update the appropriate data: (send List_Browser :answer :ADD_ITEM '(item position) '( (setq items (array-insert-pos items (1- position) item)) (send-super :add_item (send item :display_string) position) ) ) (send List_Browser :answer :ADD_ITEM_UNSELECTED '(item position) '( (setq items (array-insert-pos items (1- position) item)) (send-super :add_item_unselected (send item :display_string) position) ) ) (send List_Browser :answer :DELETE_ITEM '(item) '( ;; this is too lame to implement... requires that we compare ;; item with the result of :display_string done on every elt ;; of ivar 'items' (error "Message :DELETE_ITEM not supported in List_Browser") ) ) (send List_Browser :answer :DELETE_POS '(position) '( (setq items (array-delete-pos items (1- position))) (send-super :delete_pos position) ) ) To see how this subclassed list browser is used, and also to see how one might write a sample application in WINTERP using the object oriented features of XLISP, see ./winterp/examples/grep-br.lsp. That file implements a simple search browser based on the UN*X command 'grep'. See also ./winterp/examples/mail-br.lsp to see how you can build a simple mh-based mail browser. Finally, as another example of subclassing Motif widgets, see ./winterp/examples/radiobox2.lsp. ------------------------------------------------------------------------------- Niels Mayer -- hplabs!mayer -- mayer@hplabs.hp.com Human-Computer Interaction Department Hewlett-Packard Laboratories Palo Alto, CA. *
dlw@odi.com (Dan Weinreb) (01/12/90)
In article <1511@dinl.mmc.UUCP> noren@dinl.uucp (Charles Noren) writes:
Also, to show my ignorance, I've heard the new Common Lisp standard
includes objects. How does this compare to objects in XLISP
(unfortunatly I do not know "flavors" or the other object-oriented
variants of LISP). Is there a good reference on Common Lisp objects
that anyone would recommend?
Yes; there is a very good book on the topic:
Object-Oriented Programming in Common Lisp
by Sonya Keene
Addison-Wesley 1989
Trade paperback, 266p.