home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / EFFO / pd4.lzh / DOC / winterp.doc next >
Internet Message Format  |  1990-03-11  |  15KB

  1. From sce!mitel!uunet!tut.cis.ohio-state.edu!ucbvax!hplabs!hplabsz!mayer Thu
  2. Jan 11 12:51:21 EST 1990
  3. Article: 2234 of comp.lang.lisp
  4. Path: cognos!sce!mitel!uunet!tut.cis.ohio-state.edu!ucbvax!hplabs!hplabsz!mayer
  5. From: mayer@hplabsz.HPL.HP.COM (Niels Mayer)
  6. Newsgroups: comp.lang.lisp
  7. Subject: Re: XLISP Object Method Selectors :new and :isnew.
  8. Message-ID: <4613@hplabsz.HPL.HP.COM>
  9. Date: 10 Jan 90 07:26:26 GMT
  10. References: <1511@dinl.mmc.UUCP>
  11. Reply-To: mayer@hplabs.hp.com (Niels Mayer)
  12. Organization: Hewlett-Packard Labs, Software Technology Lab, Palo Alto, CA.
  13. Lines: 308
  14. Summary:
  15. Expires:
  16. Sender:
  17. Followup-To:
  18.  
  19. In article <1511@dinl.mmc.UUCP> noren@dinl.UUCP (Charles Noren) writes:
  20. >It's been a while since I've been on the net.  I can no longer
  21. >access comp.lang.lisp.x from our site, I suppose it went away?
  22.  
  23. it's still there...
  24.  
  25. >I've just starting playing with XLISP v2.0, particularly the
  26. >object-oriented features of it.  I've created new classes with
  27. >instance and class variables, and I've used the :new selector
  28. >to do so and it works just fine.  However, I see the :isnew
  29. >selector in the documentation and I was wondering how that works
  30. >compared to :new.
  31.  
  32. When I first looked at XLISP, I too found the documentation on the
  33. object system to be a little terse. Everything becomes much clearer
  34. once you see some examples.
  35.  
  36. I recently wrote up some documentation on XLISP's object system for
  37. use with WINTERP (an XLISP-based rapid prototyping environment for
  38. applications based on the OSF Motif widgets). The following excerpt
  39. from winterp/doc/winterp.doc may help (get winterp via anonymous ftp
  40. from expo.lcs.mit.edu:oldcontrib/winterp.tar.Z). In particular, your
  41. question about :ISNEW is answered in the "object initialization"
  42. section.
  43.  
  44.             --------------------
  45.  
  46. * Introduction to XLISP objects and Widgets.
  47.  
  48. WINTERP uses XLISP's object system as its interface to the class hierarchy
  49. of widgets provided by Motif. Specifically, each Motif widget class is
  50. represented by one or more object classes in WINTERP.  In order to best
  51. understand the capabilities of WINTERP's Motif interface, a brief review of
  52. the XLISP object system is in order. You may also want to consult the XLISP
  53. documentation ./winterp/doc/xLisp.doc for a more precise definition of the
  54. object system.
  55.  
  56. XLISP Classes describe the type of a particular object by declaring a set
  57. of variables held in each object. These "instance variables" may only be
  58. accessed by "methods" that respond to "messages" sent to the object.
  59. Methods are defined for particular classes, and functionality of other
  60. classes may be incorporated into new classes via "inheritance". From
  61. XLISP, Motif widget classes look just like normal XLISP objects -- that
  62. means that you can easily extend the functionality of Motif widgets by
  63. adding your own methods to a particular widget class. You may also use
  64. inheritance to attach your own data structures to widgets. The result is
  65. that WINTERP provides a very clean way to interactively rapid-prototype an
  66. application, while also providing mechanisms for code structuring and reuse.
  67. The latter is necessary in evolving from prototype to a structured,
  68. maintainable, and customizable deliverable.
  69.  
  70.  
  71. ** Creating new objects.
  72.  
  73. Create a new instance of a class by sending the message :NEW to
  74. <a_class_instance>:
  75.  
  76.     (SEND <a_class_instance> :NEW <parameters>)
  77.  
  78. <a_class_instance> is in fact an instance of class CLASS. Class CLASS allows
  79. you to define new class instances by specifying the instance variables and
  80. parent class of a particular class.
  81.  
  82.  
  83. ** Declaring a class.
  84.  
  85. To declare a "base class" object, that is, an object with no parent object,
  86. just send message :NEW to the object <CLASS>
  87.  
  88.     (SEND CLASS :NEW '(<ivar0> ... <ivarN>)
  89.              ['(<cvar0> ... <cvarM>)])
  90.  
  91. '(<ivar0> ... (ivarN>) are a list of symbols. Each <ivar-i> names an
  92. instance variable of the class. '(<cvar0> ... <cvarM>)]) are an optional
  93. list of variables that are shared among all instances of that particular
  94. class.
  95.  
  96.  
  97. ** Defining methods.
  98.  
  99. When a "message" is sent to an object, XLISP searches for a "method" to
  100. answer the message. A method is a piece of Lisp code that is executed when
  101. a particular message is sent to an object. Within the code of a method, all
  102. object instance and class variables are accessible. Furthermore, the symbol
  103. 'self' is bound to the object the message was sent to.
  104.  
  105. Methods are defined by sending the message :ANSWER to <a_class_instance>:
  106.  
  107.     (SEND a_class_instance :ANSWER <:msg> <parameters> <code>)
  108.  
  109. where <:msg> is a keyword symbol (a symbol with a ':' prefix) representing
  110. the message; <parameters> are the arguments given along with the message.
  111. See the documentation on "lambda lists" in /winterp/doc/xLisp.doc p.12 for
  112. details.  <code> is a list of s-expressions which get evaluated in response
  113. to a message. The lexical environment that existed for the call to :ANSWER
  114. will be used for value and functional bindings during method evaluation.
  115.  
  116. If you need to remember what the syntax is, consider the memory-helper
  117.     "this class :ANSWERs to :MESSAGE..." == (send <cls> :ANSWER :MESSAGE ...)
  118.  
  119.  
  120. ** Inheritance
  121.  
  122. So far, the object system we just described supports *encapsulation*.
  123. Encapsulation is good programming practice because it helps localize and
  124. detangle complexity. Unfortunately, encapsulation runs counter to
  125. flexibility because making flexible use of an object may require that
  126. certain groups of instance variables be accessed by different layers of new
  127. functionality. Most often, one wants to *reuse* aspects of a particular
  128. class in creating code that specializes and alters the functionality of
  129. that class. A compromise between encapsulation and flexibility is found by
  130. using *inheritance* in an object system. Inheritance is used to allow a
  131.  *subclass* to specialize the functionality of it's *parent class* (aka,
  132. the *superclass*):
  133.  
  134.     (send Class :NEW '(<ivar0> ... <ivarN>)
  135.                          '(<cvar0> ... <cvarM>)
  136.              <superclass>)
  137.  
  138. (<ivar0> ... <ivarN>) is a list of new instance variables in the subclass;
  139. (<cvar0> ... <cvarN>) is a list of new class variables in the subclass;
  140. <superclass> is a class instance representing the parent from which
  141. the new subclass inherits variables and methods.
  142.  
  143. "Inheritance" is occurring because all the instance variables of all the
  144. parent classes of the new subclass become the variables of each subclass
  145. instance. Furthermore, all methods defined on a parent class may also be
  146. used on a subclass instance. Note that while a subclass' methods can access
  147. the variables defined on the parent classes, the reverse isn't true.
  148.  
  149.  
  150. ** Object initialization.
  151.  
  152. As mentioned earlier, new object instances are created by sending the
  153. message :NEW to a class object. Sending the message :NEW to a class
  154. automatically sends message :ISNEW to the newly created instance. By
  155. default :ISNEW on an instance is a no-op method defined on class 'Object',
  156. which is the implicit [(grand)*]parent of all instances. If you want to
  157. initialize the instance/class variables of a particular class, you must
  158. define an :ISNEW method on the class.  Any parameters originally sent to
  159. the :NEW method will be passed on to the :ISNEW method and may be used to
  160. initialize an object to outside-world parameters.
  161.  
  162.  
  163. ** Example of using OOP features of XLISP with Motif widgets:
  164.  
  165. As currently implemented, the Motif class xmListWidgetClass makes it a bit
  166. baroque to create browsers (hopefully this will change in Motif 1.1).  The
  167. problem is that a "browser" is a kind of application that lends itself to
  168. object oriented techniques that are not always straightforward to support
  169. in C. One often has a collection of 'things' that one wants to display in a
  170. 'list' and perform actions on the 'thing' corresponding to the visual
  171. selection of an element in the displayed list. xmListWidgetClass will
  172. display an arrray of XmStrings in a list. When one or more elements in the
  173. list are selected, XmStrings corresponding to the selected elements are
  174. returned. Since the XmStrings you put into the list widget are not the
  175. XmStrings you get out, you must call XmStringCompare on each element of the
  176. collection of 'things' to find out which ones are selected.  Presumably,
  177. you'll want to do more than just get back an XmString...  normally one will
  178. want to access data structures associated with the XmString so as to perform
  179. an action dependent on those structures. This could be done with a lookup
  180. table, but there's also a better way...
  181.  
  182. WINTERP allows us to subclass the Motif list widget so as to make it have
  183. the kind of functionality we want. This subclass will contain an additional
  184. instance variable 'items' which is an array of arbitrary XLISP objects to
  185. be displayed in a textual browser made from the list widget. These objects
  186. can have completely different internal representations -- the only
  187. requirement is that they follow the protocol of being able to respond to
  188. the messages :DISPLAY_STRING and :DEFAULT_ACTION. :DISPLAY_STRING returns a
  189. string representation of the object to be displayed in the browser.
  190. :DEFAULT_ACTION is the action to be performed when a list item is browsed
  191. (by double clicking on the item).
  192.  
  193. The following creates the subclass <List_Browser> from superclass
  194. <XM_LIST_WIDGET_CLASS>:
  195.  
  196.     (setq List_Browser
  197.         (send Class :NEW        ;create a class inst
  198.             '(items)        ;new instance vars
  199.             '()            ;no class vars
  200.             XM_LIST_WIDGET_CLASS))    ;superclass
  201.  
  202. So now all instances of <List_Browser> will contain an instance variable
  203. <items> and will respond to all the messages understood by the
  204. XM_LIST_WIDGET_CLASS. We want our list browser to behave as described
  205. above, so we define an :ISNEW method to initialize instance variable
  206. <items> to the list of arbitrary objects to be displayed.  <items> gets
  207. initialized to an array of objects, the list widget is created, and a
  208. XmNdefaultActionCallback is setup so that a double click will send the
  209. message :DEFAULT_ACTION to the browsed item:
  210.  
  211.     ;; (send List_Browser :new <items_list> <args-for-the-list-widget>)
  212.     ;; <items_list> is a list of BROWSER_OBJECTs as described above.
  213.     ;; <args-for-the-list-widget> -- these are the arguments that
  214.     ;;       will be passed on to the list widget
  215.     ;;
  216.     (send List_Browser :answer :isnew '(items_list &rest args)
  217.           '(
  218.         (let* (
  219.                (items_end_idx (length items_list))
  220.                (display_items (make-array items_end_idx)))
  221.  
  222.           ;; initialize the 'items' instance variable so that it
  223.           ;; holds all the BROWSER_OBJECTs passed in <items_list>
  224.           (setq items (make-array items_end_idx)) ;create the array
  225.           (do (                         ;copy elts from list to array
  226.                (i    0          (1+ i))
  227.                (elts items_list (cdr elts)))
  228.               ;; loop till no more elts
  229.               ((null elts))
  230.               ;; loop body
  231.               (setf (aref items i) (car elts))
  232.               (setf (aref display_items i)
  233.                 (send (car elts) :display_string))
  234.               )
  235.  
  236.           ;; initialize the widget, passing in the browser items.
  237.           (apply 'send-super `(:isnew
  238.                        ,@args
  239.                        :xmn_selection_policy :browse_select
  240.                        :xmn_items ,display_items
  241.                        :xmn_item_count ,items_end_idx
  242.                        ))
  243.           )
  244.  
  245.         ;; set up a callback on the list widget initialized above such
  246.         ;; that a double click on the browser-item will send the
  247.         ;; message :default_action to the BROWSER_OBJECT.
  248.         (send-super :add_callback :xmn_default_action_callback
  249.                 '(callback_item_position)
  250.                 '((send (aref items (1- callback_item_position))
  251.                     :default_action))
  252.                 )
  253.         )
  254.           )
  255.  
  256.  
  257. In the above code, SEND-SUPER works just like send, except that it doesn't
  258. require you to give it the object to send the message to.  Instead, it
  259. implicitly assumes that it will be called from within a method, and will
  260. automatically send the message to a superclass of the object's class.  The
  261. (apply 'send-super ...) form is actually calling the :ISNEW (instance
  262. initializer) method on XM_LIST_WIDGET_CLASS, which actually creates the
  263. widget via XmCreateList() or XmCreateScrolledList(). The APPLY '`'
  264. (BACKQUOTE) and '&rest args' (LAMBDA LIST) features of Lisp allow us to
  265. splice in the argument list passed to the instance of List_Browser into the
  266. function that actually creates the widget. Finally, method :add_callback is
  267. the WINTERP equivalent of XtAddCallback(). See the documentation on methods
  268. on WIDGET_CLASS for more details.
  269.  
  270. The Motif List widget also defines a number of "methods" implemented as C
  271. routines such as XmListAddItem(), XmListAddItemUnselected(),
  272. XmListDeleteItem(), and XmListDeletePos(). In WINTERP, we define these as
  273. methods :ADD_ITEM, :ADD_ITEM_UNSELECTED, :DELETE_ITEM, and :DELETE_POS
  274. respectively. Since these methods modify the collection of objects
  275. represented by the list widget, we must update the internal array of
  276. objects <items> to correspond with the items displayed. We do this by
  277. intercepting those messages to the superclass of class <List_Browser> and
  278. handle them in <List_Browser> so as to update the appropriate data:
  279.  
  280.     (send List_Browser :answer :ADD_ITEM '(item position)
  281.           '(
  282.         (setq items (array-insert-pos items (1- position) item))
  283.         (send-super :add_item
  284.                 (send item :display_string)
  285.                 position)
  286.         )
  287.           )
  288.  
  289.     (send List_Browser :answer :ADD_ITEM_UNSELECTED '(item position)
  290.           '(
  291.         (setq items (array-insert-pos items (1- position) item))
  292.         (send-super :add_item_unselected
  293.                 (send item :display_string)
  294.                 position)
  295.         )
  296.           )
  297.  
  298.     (send List_Browser :answer :DELETE_ITEM '(item)
  299.           '(
  300.         ;; this is too lame to implement... requires that we compare
  301.         ;; item with the result of :display_string done on every elt
  302.         ;; of ivar 'items'
  303.         (error "Message :DELETE_ITEM not supported in List_Browser")
  304.         )
  305.           )
  306.  
  307.     (send List_Browser :answer :DELETE_POS '(position)
  308.           '(
  309.         (setq items (array-delete-pos items (1- position)))
  310.         (send-super :delete_pos position)
  311.            )
  312.          )
  313.  
  314. To see how this subclassed list browser is used, and also to see how one
  315. might write a sample application in WINTERP using the object oriented
  316. features of XLISP, see ./winterp/examples/grep-br.lsp.  That file
  317. implements a simple search browser based on the UN*X command 'grep'. See
  318. also ./winterp/examples/mail-br.lsp to see how you can build a simple
  319. mh-based mail browser. Finally, as another example of subclassing Motif
  320. widgets, see ./winterp/examples/radiobox2.lsp.
  321. -------------------------------------------------------------------------------
  322.         Niels Mayer -- hplabs!mayer -- mayer@hplabs.hp.com
  323.           Human-Computer Interaction Department
  324.                Hewlett-Packard Laboratories
  325.                   Palo Alto, CA.
  326.                    *
  327.  
  328.  
  329.