brian@vuse.vanderbilt.edu (Brian Antao) (04/22/91)
Hi, Having been a moderately heavy user of Smalltalk-80 (ParcPlace sys, Objectworks), I had a suggestion that may be fairly easy to implement and would save some programming effort. Often one needs to define access methods with each Class definition, these methods simply return the current value of an instance variable. The suggested feature would be, that the compiler/ interpreter, by default inserts these methods corresponding to each instance variable specified in the Class definition. Each time the Class definition is recompiled, the appropriate access methods should be added/deleted if any instance variables are added/deleted. One could also argue for a similar feature to also support class variable access. I vaguely remember that some of the earlier Smalltalk-80 versions, 1.xx or 2.2 that did something on the lines of the above functionality. Do other smalltalk-80 users feel positively about this ? ParcPlace Objectworks developers: would you take up on this ?? or is this feature available as an addOn like the BOSS, with the ObjectKit ? -- ------------------------------------------------------------------------------- Brian Antao, |FAX: 615-343-6449 Vanderbilt University, |INET: brian@vuse.vanderbilt.edu Department of Electrical Engg., |UUCP: ...!uunet!vuse!brian
dawg6844@uxa.cso.uiuc.edu (Race Bannon) (04/22/91)
brian@vuse.vanderbilt.edu (Brian Antao) writes: >an instance variable. The suggested feature would be, that the compiler/ >interpreter, by default inserts these methods corresponding to each instance >variable specified in the Class definition. Each time the Class definition is >recompiled, the appropriate access methods should be added/deleted if any >instance variables are added/deleted. One could also argue for a similar >feature to also support class variable access. This would not be difficult to add, just aesthetically unpleasant. Why? One of the primary ideas behind 'objects' is the encapsulation of data with the code that manipulates it. Why is this good? If used properly, it minimizes the 'side-effects' that can occur with other programming paradigms. In SmallTalk, no object can manipulate another's state unless you 'do it on purpose', by constructing the access methods to do so. If access methods were automatically generated for every private variable, then this breaks down; every object can diddle with the state of every other. Yes, frequently you need to write several access methods for several private variables of an object, but if you are finding that you need to make ALL of your instance variables public, then perhaps you need to rethink your design. -- _______________________________________________________________________________ Dan Walkowski | To understand recursion, Univ. of Illinois, Dept. of Comp. Sci. | you must first understand recursion. walkowsk@cs.uiuc.edu |
pkr@media03.UUCP (Peter Kriens) (04/22/91)
Dan Walkowski writes: > If access methods were automatically generated for every private variable, > then this breaks down; every object can diddle with the state of every other. > Yes, frequently you need to write several access methods for several private > variables of an object, but if you are finding that you need to make ALL of > your instance variables public, then perhaps you need to rethink your design. I basically agree with this statement, but I have found that it is much more tru in technical software then information technology. The last moths{ I have been trying several times to build a program that uses a SQL database as backend and a smalltalk front end. The problem is that in those environments you basically need access to most instance variables.(the origininal database fields). Eiffel has a very nice solution for this. It allows an instance variable to be exported in the header (Smalltalk could also do that in the class definition, you could for example place a colon behinde the definition of the instance variable). So in Eiffel you can easely access those often used instance variables. The good thing is that if you decide later on to replace the instance access with a procedure, because the implementation changes, nobody notices because the syntax stays the same. Sounds like a feature I could love in Smalltalk. Unfortunately you cannot access the compiler in Digitalk smalltalk, otherwise it could be done. Peter Kriens pkr@media01
voss@cs.uiuc.edu (Bill Voss) (04/22/91)
In article <1991Apr21.221149.8057@vuse.vanderbilt.edu> brian@vuse.vanderbilt.edu (Brian Antao) writes: > Often one needs to define access methods > with each Class definition, these methods simply return the current value of > an instance variable. The suggested feature would be, that the compiler/ > interpreter, by default inserts these methods corresponding to each instance > variable specified in the Class definition. Methods such as these are the Smalltalk equivalent of the GOTO statement. Yes I have written and used such methods. They are especially useful during the early exploratory/debugging stages. However, they violate the fundamental principles of encapsulation which make OOP so useful. I definitely do NOT want such a "feature" added as a standard part of the language. I am not saying don't use such methods. I am saying that until you finish eliminating such methods from a design, you should know you are not finished creating your design. Exception: Objects which exist solely to "encapsulate" external interfaces must frequently violate this rule, until you can redesign the external part of the interface. This is in large part a religious/style question, and I am obviously a "purist" on this one. Some heretic ;-) will probably post a goodie which does almost exactly what you want. It looks like a rather simple problem (at least in st80). Bill Voss <voss@cs.uiuc.edu>
boissier@irisa.fr (franck boissiere) (04/22/91)
From article <2270@media03.UUCP>, by pkr@media03.UUCP (Peter Kriens): > Dan Walkowski writes: [stuff deleted] > a feature I could love in Smalltalk. Unfortunately you cannot access the > compiler in Digitalk smalltalk, otherwise it could be done. > You do not need to modify the compiler to do this (unless you want to change the syntax which is what Peter proposes). The key is to define a new class definition message which allow you to specify which variables are to be exported. The key issue is to track all class modification to add/remove the methods according to instance variable additions/removals. In fact when you do an accept in the Browser text pane the Compiler simply evaluate the expression printed. -- Franck BOISSIERE boissier@irisa.irisa.fr C.C.E.T.T. B.P. 59 boissiere@ccett.fr 35512 CESSON SEVIGNE CEDEX FRANCE
knight@mrco.carleton.ca (Alan Knight) (04/22/91)
In article <2270@media03.UUCP> pkr@media03.UUCP (Peter Kriens) writes: >Dan Walkowski writes: >> If access methods were automatically generated for every private variable, >> then this breaks down; every object can diddle with the state of every other. >> Yes, frequently you need to write several access methods for several private >> variables of an object, but if you are finding that you need to make ALL of <stuff omitted> >Eiffel has a very nice solution for this. It allows an instance variable >to be exported in the header (Smalltalk could also do that in the >class definition, you could for example place a colon behinde the >definition of the instance variable). So in Eiffel you can easely access those often >used instance variables. The good thing is that if you decide later on >to replace the instance access with a procedure, because the implementation >changes, nobody notices because the syntax stays the same. Sounds like >a feature I could love in Smalltalk. Unfortunately you cannot access the >compiler in Digitalk smalltalk, otherwise it could be done. > >Peter Kriens >pkr@media01 A nearly equivalent feature, and one which preserves a little more encapsulation than automatically writing all acces methods is to allow them to be written on command, one at a time. i.e. have a menu item which writes access methods for a single instance variable. I've seen this done in Smalltalk/V, and it could easily be done in Smalltalk-80. The disadvantage of this method is that it discourages comments, particularly a problem in /V where there are no class comments. Coming across the automatically written message glorch: aValue "Set the value of the instance variable glorch to aValue" glorch := aValue. is annoying (to say the least), when there is no indication what kind of a thing glorch is or what it is used for. -- -- Alan Knight knight@mrco.carleton.ca +1 613 788 5783 Support Dept. of Mechanical and Aeronautical Engineering the Carleton University, Ottawa, Ontario, Canada, K1S 5B6 LPF
dawg6844@uxa.cso.uiuc.edu (Race Bannon) (04/22/91)
boissier@irisa.fr (franck boissiere) writes: >From article <2270@media03.UUCP>, by pkr@media03.UUCP (Peter Kriens): >> Dan Walkowski writes: >[stuff deleted] >> a feature I could love in Smalltalk. Unfortunately you cannot access the >> compiler in Digitalk smalltalk, otherwise it could be done. >> please pay attention to how you quote in the future. I did NOT say the above. I was arguing _against_ automatic accessing methods. I have never even used DigiTalk. (I use PP) -- _______________________________________________________________________________ Dan Walkowski | To understand recursion, Univ. of Illinois, Dept. of Comp. Sci. | you must first understand recursion. walkowsk@cs.uiuc.edu |
nvi@mace.cc.purdue.edu (Charles C. Allen) (04/22/91)
Actually, I think being able to tell the compiler that "private" methods are just that would solve this and other problems relatively cleanly. I'm not so enamored of C++'s "protected", but others feel it would be a good idea as well. Charles Allen Internet: cca@physics.purdue.edu Department of Physics HEPnet: purdnu::allen, fnal::cca Purdue University Bitnet: cca@fnal.bitnet West Lafayette, IN 47907-1396 talknet: 317/494-9776
brian@vuse.vanderbilt.edu (Brian Antao) (04/23/91)
In article <1991Apr22.003659.18658@ux1.cso.uiuc.edu> dawg6844@uxa.cso.uiuc.edu (Race Bannon) writes: >brian@vuse.vanderbilt.edu (Brian Antao) writes: >>an instance variable. The suggested feature would be, that the compiler/ >>interpreter, by default inserts these methods corresponding to each instance >>variable specified in the Class definition. Each time the Class definition is >>recompiled, the appropriate access methods should be added/deleted if any >>instance variables are added/deleted. One could also argue for a similar >>feature to also support class variable access. > >This would not be difficult to add, just aesthetically unpleasant. Why? >One of the primary ideas behind 'objects' is the encapsulation of data with >the code that manipulates it. Why is this good? If used properly, it >minimizes the 'side-effects' that can occur with other programming paradigms. There is a limitation in smalltalk, in that one cannot explicitly classify instance variables to be public or private. If this feature exists, such as in C++, then the access methods would be generated only for those instance variables designated as public, and need access methods anyway. This would satisfy the encapsulation constraints as well. >In SmallTalk, no object can manipulate another's state unless you >'do it on purpose', by constructing the access methods to do so. >If access methods were automatically generated for every private variable, >then this breaks down; every object can diddle with the state of every other. An object would be able to access the state of another object only if it has been instructed to do so. ie via explicit coding. In such cases you need some aspects of an object that is maintained in an instance variable. Th only way to acces and update/modify the state is via an access method. >Yes, frequently you need to write several access methods for several private >variables of an object, but if you are finding that you need to make ALL of >your instance variables public, then perhaps you need to rethink your design. At the current status of smalltalk, there is no way of seperating private and public internal (instance variables), hence the default notion of access methods for all. Which also bring up the issue, DO we need to distinguish Private and Public instance variables in smalltalk ?, -- Brian Antao, Dept. of Electrical Engg., |INET: brian@vuse.vanderbilt.edu Vanderbilt University, Nashville, TN, USA 37235 Disclaimer: "Artificial Intelligence" - an oxymoron ? -------------------------------------------------------------------------------
voss@cs.uiuc.edu (Bill Voss) (04/23/91)
Teach me to be careless when I post. ;-) Naah.. >I wrote: > I am not saying don't use such methods. I am saying that until > you finish eliminating such methods from a design, you should know you are > not finished creating your design. Exception: Objects which exist solely > to "encapsulate" external interfaces must frequently violate this rule, > until you can redesign the external part of the interface. I still maintain that using access methods is a strong indication that the programmer has goofed (or not finished). However I forgot the BIG exception. The "private" method category is almost always filled with "access" methods, and I tolerate this (in MY code) PROVIDED all methods in the "private" category are only used by other methods in the SAME CLASS (or maybe reluctantly subclasses). [NOTE: Access methods in the "private" category can also almost always be avoided by using double dispatching. I generally prefer to use double dispatching, but consider a more direct interface acceptable when confined to PRIVATE methods.] My objection is not an algorithm, but it is in my experience a very good heuristic. When I call another class's access methods, I almost always find I should have opened another browser, and started adding methods to the other class instead. Thus as a "purist" on this question, I consider access methods similar to the goto statement. Occasionally needed, but usually abused. Bill Voss - the purist - <voss@cs.uiuc.edu>
knight@mrco.carleton.ca (Alan Knight) (04/23/91)
-- A couple of people have talked about the undesirability of access methods for instance variables, including: In article <VOSS.91Apr22183834@laslo.cs.uiuc.edu> voss@cs.uiuc.edu (Bill Voss) writes: >My objection is not an algorithm, but it is in my experience a very good heuristic. >When I call another class's access methods, I almost always find I should have >opened another browser, and started adding methods to the other class instead. > >Thus as a "purist" on this question, I consider access methods similar to >the goto statement. Occasionally needed, but usually abused. > >Bill Voss - the purist - <voss@cs.uiuc.edu> I think that this is a rather extreme position. Gratuitously accessing the representation of another class is undesirable, but often that representation corresponds to information about the class that a client might legitimately want to know. If access methods are usually an indication that the programmer has goofed then what about Point x, y Rectangle origin, corner, extent Line slope, or Line coefficients All of these represent information that I might reasonably want to know about an object, but they may easily correspond to pieces of the representation. Then again, they may not, and that's the point of encapsulation. -- -- Alan Knight knight@mrco.carleton.ca +1 613 788 5783 Support Dept. of Mechanical and Aeronautical Engineering the Carleton University, Ottawa, Ontario, Canada, K1S 5B6 LPF
mjohnson@sirius.acs.calpoly.edu (Mark S. Johnson) (04/23/91)
As a relative newcomer to Smalltalk, I am very interested in this debate. I'd like to start some serious coding in the near future and I'd like to get off on the right foot "stylistically." I tend to agree with the folks who oppose access methods, but what about when using simple "readonly" methods to extract information from a "record-like" object? If the object Mark below is a "record-like" object containing personal information about me, what's wrong with this type of access Mark eyeColor to check my eye color? Note that Mark does not necessarily have an instance variable named eyeColor. Am I being a dope about this? As an aid to me learning Smalltalk in a vacuum, can anyone recommend a Smalltalk "style guide" which addresses these issues? Thanks for listening, Mark -- Mark S. Johnson mjohnson@stn9.me.calpoly.edu (129.65.19.9)
dawg6844@uxa.cso.uiuc.edu (Race Bannon) (04/23/91)
Because Smalltalk lacks syntax for describing 'public' and 'private' variables does not mean it lacks these concepts. ALL instance variables are 'private' unless explicitly made otherwise by adding an access method. -- _______________________________________________________________________________ Dan Walkowski | To understand recursion, Univ. of Illinois, Dept. of Comp. Sci. | you must first understand recursion. walkowsk@cs.uiuc.edu |
voss@cs.uiuc.edu (Bill Voss) (04/24/91)
> but what about when using > simple "readonly" methods to extract information from a "record-like" > object? My objection to access methods is in part because they do encourage "record-like" objects, instead of more active objects. The use of access methods tends to indicate that the programmer is using a Class the way a Pascal programmer would use a Record. Instead of having the object do the work, the programmer has the object store information, then regurgitate raw information which code outside of the object manipulates. > If the object Mark below is a "record-like" object containing > personal information about me, what's wrong with this type of access > > Mark eyeColor > > to check my eye color? Note that Mark does not necessarily have an > instance variable named eyeColor. Maybe alot, maybe nothing. To answer your question I need to know WHY you want to know Mark's eyeColor. Do you want to see if Mark has the same color eyes as Bob? Then you should use Mark sameColorEyesAs: Bob ifTrue:..... instead of m <- Mark eyeColor. b <- Bob eyeColor. m = b ifTrue:..... Do you want to draw a picture of Mark's eyes? Then you should use Mark drawEyesOn: someDisplayable at: somePoint andSoOn:.... instead of m <- Mark eyeColor. blaBlaBla... color: m. The key point is that code which needs to know about Mark's eye color should be INSIDE the class which keeps track of Mark's eye color. This could be the class Human, or Human could contain two instances of class Eye. In that case the class Human would split up "drawEyesOn:" for example into two "drawYourselfOn:" messages, one for each eye. The code which needs to know about Mark's eyes should almost never be located in some external method which simply send's Mark an "eyeColor" message. Thus in my (never humble ;-) opinion, if Mark is a "record-like" object, you have already made a fundamental mistake. You should have used Pascal/.... which directly supports Records instead. (but please don't Smalltalk is much nicer. :-) > As an aid to me learning Smalltalk in a vacuum, can anyone recommend a > Smalltalk "style guide" which addresses these issues? I don't think such a book exists. Just monitor comp.lang.smalltalk for messages containing words like "extreme" "wrong" .... ;-) Exercise: Assume two equivalent systems, both need to determine in say twenty different places wether or not Mark and Somebody have the same color eyes. One system uses my "sameColorEyesAs:" method, and one system uses the "eyeColor" method. Your job (should you choose to accept it) is to change one of the two systems to support people with different color eyes. (Someone with a brown left eye, and a green right eye does NOT have the same color eyes as someone with TWO green eyes.) Which system would you rather change? Then (try to) write your code that way to begin with. Bill Voss -- voss@cs.uiuc.edu
dawg6844@uxa.cso.uiuc.edu (Race Bannon) (04/24/91)
mjohnson@sirius.acs.calpoly.edu (Mark S. Johnson) writes: >object? If the object Mark below is a "record-like" object containing >personal information about me, what's wrong with this type of access > Mark eyeColor >to check my eye color? Note that Mark does not necessarily have an >instance variable named eyeColor. >Am I being a dope about this? I'm much more of a moderate than my esteemed colleague Mr. Voss. I don't advocate having NO accessing methods, just as few as possible. -- _______________________________________________________________________________ Dan Walkowski | To understand recursion, Univ. of Illinois, Dept. of Comp. Sci. | you must first understand recursion. walkowsk@cs.uiuc.edu |
kentb@argosy.UUCP (Kent Beck) (04/24/91)
I don't wish to flame the original poster, but I think automatic compilation of access methods makes it entirely too easy to fall back into "rape and plunder" mode, where are object are expensive data structures. As Dave Ungar has pointed out, objects are nothing more than their behavior. If part of their behavior is to return another object, fine, but I like the way the annoyance of defining an instance variable access method forces me to stop and think, "Should this object be doing more, so that other objects would no longer need access to this variable?" This flies in the face of the "access method for all variables" camp, but to me the value of added encapsulation far outweighs the minor hassle of changing instance variable references to messages if such a change becomes necessary. Kent
voss@cs.uiuc.edu (Bill Voss) (04/24/91)
I wrote a few messages ago: >When I call another class's access methods, I almost always find I should have >opened another browser, and started adding methods to the other class instead. Alan Knight argues the following are counter examples > Point x, y > Rectangle origin, corner, extent > Line slope, or Line coefficients > All of these represent information that I might reasonably want to > know about an object, but they may easily correspond to pieces of > the representation. Then again, they may not, and that's the point of > encapsulation. Point is a particular favorite of mine, because I goofed BIG using class Point when I started using Smalltalk. Back then I was very very new to Smalltalk, and very very timid/reluctant to alter/extend the existing code. So I was writing something which needed a Point. Hey I thought to myself, I'll bet a class Point already exists. Search for "Point" yep, sure does. Lets see how this works....ah ACCESSING methods x x: y y: thats great. I understand these methods. I can read and set the x and y values for a point, just what I need. Wow they were right, you can reuse code in Smalltalk. This is great. :-) For the next few weeks, if I needed the distance between two points, I'ld go x1 <- point1 x. y1 <- point1 y. and so forth, plugging my temporary variables into the distance formula we learned in High School. I was using Point as a simple two element array. Obviously I should have been using "point1 dist: point2." instead. However, NOT because dist: already exists. I should have been using dist: EVEN IF I NEEDED TO WRITE IT. Unlike my code, the "dist:" method was in the right place. It needs to know about the internals of a Point, so it is located INSIDE the class Point. The method "dist:" is in my opinion a very GOOD example of how to do things. However, the class Point is not such a good class. The Point class seems to encourage programmers to break encapsulation. Exercise 1: Make a wild guess as to how frequently code in the Smalltalk-80 image needs to determine the distance between two points. invoke "senders" on the method "dist:" invoke "senders" on the method "x" How are programmers doing it? Exercise 2: A new faster way of computing the distance between two points has been found. Change all existing code in the Smalltalk-80 hierarchy to use the new method. Exercise 3: Comment on the results of Exercise 1 based on your experience in Exercise 2. So I have shown that accessing the x and y instance variables of class Point MAY be wrong. Hopefully we can all at least agree on that much? :-) ------------------------------------------------------------------------------- Now is it ALWAYS wrong to use access methods? I do NOT contend that it is ALWAYS wrong. I think it must be judged on a case by case basis. However, I do contend that it is USUALLY wrong. I have not yet seen anyone post a good counter example. They say this or that instance variable is "obviously" an exception, but they don't show WHY they need to access that particular variable. Without knowing WHY I can't tell wether they are correct or not. (Hmm looks like rain... I might even get flooded now. ;-) ------------------------------------------------------------------------------- The next obvious question is "What do you do instead?" ;-) So glad you asked. ;-) It is my contention that instead of using something like
voss@cs.uiuc.edu (Bill Voss) (04/24/91)
For some reason GNUS/nntpd keeps dropping the tail of my posting with the message "writing to process: no more processes, nntpd" So here is that final section, ONE MORE TIME. -Bill ------------------------------------------------------------------------------- The next obvious question is "What do you do instead?" ;-) So glad you asked. ;-) It is my contention that instead of using something like x1 <- point x. y1 <- point y. anObject myThingX: x1 Y: y1. or in one line anObject myThingX: (point x) Y: (point y). in those cases where you legitimately need to know x and y, outside of Point, you should instead write a Point method: doMyThingOn: anObject ^anObject myThingX: x Y: y. I consider this CLEANER, more READABLE, more MAINTAINABLE, and much harder to abuse than a straight ACCESS technique. The fact that it usually involves both less programmer typing, and fewer runtime message sends is a nice bonus. -Bill Voss <voss@cs.uiuc.edu>
knight@mrco.carleton.ca (Alan Knight) (04/24/91)
In article <VOSS.91Apr23194435@laslo.cs.uiuc.edu> voss@cs.uiuc.edu (Bill Voss) writes: >I wrote a few messages ago: > >When I call another class's access methods, I almost always find I should have > >opened another browser, and started adding methods to the other class instead. > >Alan Knight argues the following are counter examples >> Point x, y >> Rectangle origin, corner, extent Lots of stuff with some very good points omitted to try and reduce length. I'll summarize what I thought the good points were. Summaries begin with --, quotes start each line with >, plain text is my comments. -- Lots of people access x and y values for points when they really --shouldn't, either using point as an array of two values or to compute --things that should have been made into methods for Point, with dist: --being the example. > Obviously I should have been using "point1 dist: point2." instead. > However, NOT because dist: already exists. I should have been using > dist: EVEN IF I NEEDED TO WRITE IT. Unlike my code, the "dist:" > method was in the right place. It needs to know about the internals > of a Point, so it is located INSIDE the class Point. Absolutely. A good example of when access methods are abused and code duplicated for no good reason. -- The class Point encourages programmers to break encapsulation. >Exercise 2: A new faster way of computing the distance between two > points has been found. Change all existing code in the > Smalltalk-80 hierarchy to use the new method. Yes, but this unlikely for this particular example. Far more likely is that you will decide you want to use a different metric for some of your code, in which case isn't nearly as easy. e.g. This UI code doesn't really need precise distances, a Manhattan metric is fine to test if the mouse is "close enough". The method dist: should have been used anyway, but as much to avoid duplication of code as to avoid accessing representation. It also seems to me that the representation part is somewhat of a side issue. If points were represented in polar coordinates would x, y, x:, y: suddenly become all right to use? Does caching a previously computed property inside an object make it wrong to access that property? It seems as if the issue is whether objects should return properties of themselves, or if objects should always be dealt with as an indivisible whole, where most messages don't return anything more complex than a boolean. > I do NOT contend that it is ALWAYS wrong. > I think it must be judged on a case by case basis. > However, I do contend that it is USUALLY wrong. > > I have not yet seen anyone post a good counter example. > They say this or that instance variable is "obviously" > an exception, but they don't show WHY they need to > access that particular variable. Without knowing WHY > I can't tell wether they are correct or not. > OK, here are some examples, still using x and y. Suppose that I have a set of points, and that I want to sort them. There's no canonical sorting order for points, so I have to define one. I could define my sortBlock as [ :point1 :point2 | point1 x <= point2 x] OR [ :point1 :point2 | point1 y <= point2 y]. On the other hand, to avoid accessing the representation, I could write [:point1 :point2 | point1 xIsLessThanOrEqualXFor: point2] I contend that there is no additional encapsulation in the second example, and no good reason to use it. Of course for generality there should really be a single sort block. [:point1 :point2 | (point1 componentInDirection: aVector) <= (point2 componentInDirection: aVector)] which is no longer really an access method, but the efficiency gain is significant enough that I believe a lot of people would write one of the preceeding forms. Another, and perhaps a better example is where one needs to know about internals of more than one class at a time. For example, suppose that I have a geometric object and I need to do some mathematics on it (I'm specifically looking at code for doing finite element analysis as I write this, but think of it in general). This mathematics probably involves plugging x and y coordinates of some or all of the points into some fairly complicated equations. Right now this code is in the finite element, which is (IMHO) where it belongs, since it has to know a lot more about the details of the finite element than about the particular points, but it does need to know x's and y's. I certainly don't think that having the methods Point>> findBasisFunctionsForEightNodeBrick: aBrick findBasisFunctionsForFourNodeQuad: aQuad findBasisFunctionsForTetrahedron: aTetrahedron etc. etc. is in any way a cleaner solution. > It is my contention that instead of using something like > x1 <- point x. > y1 <- point y. > anObject myThingX: x1 Y: y1. > in those cases where you legitimately need to know x and y, > outside of Point, you should instead write a Point method: > > doMyThingOn: anObject > > ^anObject myThingX: x Y: y. > > I consider this CLEANER, more READABLE, more MAINTAINABLE, and > much harder to abuse than a straight ACCESS technique. > > The fact that it usually involves both less programmer typing, > and fewer runtime message sends is a nice bonus. Well, I have to admit that I don't consider this CLEANER, more READABLE, or more MAINTAINABLE, although it might arguably be harder to abuse. Its main advantage seems to be that it gives you an idea of who might be getting at the representation of point with a little more detail than might be seen from senders of x or y. It doesn't seem to answer my previous example where I want x and y values for several points without a great deal of convolution. I can see circumstances where this would be useful, but I think it can wind up adding a lot of extraneous methods to Point. In summary, I don't think we disagree qualitatively. Access to the representation should be carefully watched, and you should always think twice before doing it. That doesn't mean that it's usually a mistake, or that you should put methods in the "wrong" place to get around it. A method should be put in the class where it needs to know the most about the internals, and knowledge of the internals of other classes should be minimized, but cannot always be avoided. -- -- Alan Knight knight@mrco.carleton.ca +1 613 788 5783 Support Dept. of Mechanical and Aeronautical Engineering the Carleton University, Ottawa, Ontario, Canada, K1S 5B6 LPF
eat@helmut.scs.carleton.ca (Eric Arseneau) (04/25/91)
In article <VOSS.91Apr23201829@laslo.cs.uiuc.edu>, voss@cs.uiuc.edu (Bill Voss) writes: |> |> For some reason GNUS/nntpd keeps dropping the tail of my posting |> with the message "writing to process: no more processes, nntpd" |> So here is that final section, ONE MORE TIME. |> -Bill |> ------------------------------------------------------------------------------- |> The next obvious question is "What do you do instead?" |> ;-) So glad you asked. ;-) |> |> It is my contention that instead of using something like |> |> x1 <- point x. |> y1 <- point y. |> anObject myThingX: x1 Y: y1. |> or in one line |> anObject myThingX: (point x) Y: (point y). |> |> in those cases where you legitimately need to know x and y, |> outside of Point, you should instead write a Point method: |> |> doMyThingOn: anObject |> |> ^anObject myThingX: x Y: y. |> |> I consider this CLEANER, more READABLE, more MAINTAINABLE, and |> much harder to abuse than a straight ACCESS technique. |> |> The fact that it usually involves both less programmer typing, |> and fewer runtime message sends is a nice bonus. |> |> -Bill Voss <voss@cs.uiuc.edu> This goes without saying. But saying that access methods are a no-no is absolutely wrong. This is easy to show by simply stating that a user of an object has absolutely no way to know what is being returned, an instance variable or a function of some other objects. This is what encapsulation gives you, and it suppors CLEANER, READABLE and MAINTAINABLE as the users do not know ho they are implemented. If a function makes sense to do on a particular object, then you add it to that object's class. If myThingOn: is something that is general for a point then it is totally obvious that you shoudl implement it under Point, but this does not rule out access methods. If at a later point I change the representation of my objects, then I still have to provide the accessing methods I had before, so if a method used to return an instance variable, and it no longer does, who's to know the diference. The point is that the method still has to be supported in the new representation because it was part ofg the public interface to your objects before, and you're still repsonsible for supporting all of the previous operations, even if the representation chenages. When I am writing a class, I write an access method for all of my instance variables, making some private and some public. Instead of referencing an instance variable directly, I will send myself the accessor message` to get it's value. That way when I change the representation of my class later on, all of my other operations will still work as long as I still support the old accessor messages, but mapped to something appropriate. This makes it much easier for the implementor (not the user) of a class to change his represenation and still have things working somewhat, maybe not as efficient as it should be, but he can go back later and replace the senders of the old accessors and update them to the new representation. THE POINT: Acessor methods are definitely OK !!!
voss@cs.uiuc.edu (Bill Voss;;;356-4652;jerry.cs.uiuc.edu) (04/25/91)
Trying again with a different machine. Sorry if the other one made it part way out. -- HUGE SIGH. ------------------------------------------------------------------------------ First let me say thanks to all my fellow debate partners in this string, both the posters, and the emailers. This is definitely the most fun I've had in comp.lang.smalltalk for months. We have even managed to disagree without slipping into a flame war, in my opinion this string is an example of USENET at its finest. Now replying to Alan's reply to my....... > OK, here are some examples, still using x and y. I wish I'ld never accepted Point as an example. SIGH. > Suppose that I have a set of points, and that I want to sort them. > There's no canonical sorting order for points, so I have to define > one. I could define my sortBlock as > > [ :point1 :point2 | point1 x <= point2 x] > OR [ :point1 :point2 | point1 y <= point2 y]. > > On the other hand, to avoid accessing the representation, I could > write > [:point1 :point2 | point1 xIsLessThanOrEqualXFor: point2] > > I contend that there is no additional encapsulation in the second > example, and no good reason to use it. Of course for generality there > should really be a single sort block. > [:point1 :point2 | (point1 componentInDirection: aVector) <= > (point2 componentInDirection: aVector)] > which is no longer really an access method, but the efficiency gain is > significant enough that I believe a lot of people would write one of > the preceeding forms. PP.R4 class "Point" method category "comparing" method "<= aPoint" method comment says "Answer whether the receiver is 'neither below nor to the right' of aPoint." So you can stick PP.R4 Point's directly into a sorted collection. If you need a different canonical sorting order, then yes I most definitely would suggest adding a simple method "xyzCompare:" to class point. Then use the simple and readable sort block [:p1 :p2 |p1 xyzCompare: p2]. Both methods will obviously work, but there is an implicit difference in encapsulation. > [ :point1 :point2 | point1 x <= point2 x] Would you use that sort block if you KNEW that the internal representation of a Point was NOT based on instance variables X and Y? I like my code to run as fast as possible, so I would try to avoid converting Points needlessly. As an external user asking for x and y, I have no business worrying about a Point's internal representation. As an xyzCompare: method inside of Point I can legitimately take Point's internal representation into consideration. Thus using xyzCompare: I can legitimately make efficiency decisions based on Point's internal representation. I`ld also either name xyzCompare: something like orderByGodzilianSpiral: in this case I've already written my GodzilianSpiral code when I need it again OR projectHack12BSorter: in which case WHEN I later decide I didn't really need that infinite precision asymptotically corrected aglutinated comparison, and could instead get by with an 8 way truncated fast shuffle I only need to change code in ONE PLACE. (I can also find all such users quickly with "senders".) ============================================================================== > Another, and perhaps a better example is where one needs to know about > internals of more than one class at a time. For example, suppose that > I have a geometric object and I need to do some mathematics on it (I'm > specifically looking at code for doing finite element analysis as I > write this, but think of it in general). This mathematics probably > involves plugging x and y coordinates of some or all of the points > into some fairly complicated equations. > Right now this code is in the finite element, which is (IMHO) where > it belongs, since it has to know a lot more about the details of the > finite element than about the particular points, but it does need to > know x's and y's. I certainly don't think that having the methods > > Point>> findBasisFunctionsForEightNodeBrick: aBrick > findBasisFunctionsForFourNodeQuad: aQuad > findBasisFunctionsForTetrahedron: aTetrahedron > > etc. etc. is in any way a cleaner solution. I've never done any finite element analysis, I don't think I could even tell you what it is used for. (My, my, the state of my education.... ;-) That said, it sounds as though you are treating Points as simple Records here. Extracting out the raw data they contain, and plugging that data into a mathematical formula. There is nothing apriori wrong with doing this. In such a situation the difficult portion is usually the mathematics, not the programming. However, I wouldn't describe it as object oriented programming either, it is programming in an imperative style using an object oriented language. I know if someone gave me a problem which involved complex mathematical formulas I'ld do the same thing. I'ld simply plug the formulas into my program. Not all problems require a hammer, and not all problems require object oriented programming. This seems to be one of those cases where imperative is better, and imperative programming definitely DOES work better with access methods. So I need to update my exceptions list. Using ACCESS methods is almost always undesirable EXCEPT: 1) As class "private" methods. 2) To encapsulate an external interface you can't easily change. 3) To implement something not easily expressed in an object oriented style. Such as a complex mathematical formula better expressed in an imperative style. Bill Voss -- voss@cs.uiuc.edu -- Graduate Student in Computer Science P.S. Thinking about it a wee bit more, perhaps you could handle a mathematical formula in a rather object oriented style by making the formula itself a class. You feed it what it needs at instance creation, and then eventually ask it for its value. You would build the big formula with instances of smaller formulas.... hmmm. If anyone out there has an OBJECT ORIENTED way of handling complex mathematical formulas, please start another string and tell us about it. -- Bill Voss INTERNET: billvoss@uiuc.edu UUCP: uunet!uiucuxc!uiucdcs!voss
tma@m5.COM (Tim Atkins) (04/25/91)
Seems to be a lot of hype around this question so I'll post my own. One point that has been neglected somewhat is that well designed classes tend to have named instance variables that clearly encapsulate data-ish parts of the problem domain. Sending a message to get at this held data via a request to its encapsulator is perfectly ok. If the encapsulator did not want to allow access (read only here) to this data that class would not have done so. From an external point of view it is not knowable whether a particular method just returns the value of an instance variable or computes its result. This is encapsulation! To me it has nothing whatsoever to do with what in fact is the implementation of the method. Also it would be silly to make up some new name for the method that was not the instance variable name. I believe most posters grant this. There is a valid point that over-reliance on such data-ish features often fails to produce a good design. But this is more a design viewpoint issue than an implementation guideline. It is all too easy to view the world as a bunch of data structures like we learned back in the dark ages instead of as a set of "objects" capable of responding to methods. For this reason I tend to get a bit frustrated with OOD and OOA tools that concentrate early on data relationships in the design. Another point that has not been made is that good OO programming style asserts that instance variables should always be accessed indirectly through methods, even by methods of the same class and its subclasses. One reason is that any other approach makes the system very fragile and hard to modify. Given that this is so, it makes a great deal of sense to have automatically generated access methods available. - Tim Atkins
pkr@media03.UUCP (Peter Kriens) (04/25/91)
voss@cs.uiuc.edu (Bill Voss) writes (after giving an example about point not using x,y for calculating the distance between two points) Now is it ALWAYS wrong to use access methods? I do NOT contend that it is ALWAYS wrong.v:Soon I think it must be judged on a case by case basis. However, I do contend that it is USUALLY wrong. 1: I think a basic quality of object oriented design is defining a "functionality" only in place. Calculation of the distance between points should therefore be placed in one location and that is the Point class. Your example violates this first rule. I cannot in the world see what this example has to do by accessing through a method of the instance variable. 2: Instance variables have a natural tendenncy to contain the most relevant information regarding an object. So it seems quite natural to me that they should be accessible outside the object. What I dont want is that once I change the implementation (e.g. change a Point from x,y to polar coordinates) that my clients break. As long as the instance variables are accessed through methods (and i dont seen another way to do it in Smalltalk) I can always keep the same interface. So what is wrong in have the instance variable access methods? 3: I do not promote to make EACH instance variable automatically accessible, but I think it would be very nice to allow variables to be exported like in Eiffel. Peter Kriens pkr@media01.uucp
knight@mrco.carleton.ca (Alan Knight) (04/25/91)
In article <VOSS.91Apr24215444@jerry.cs.uiuc.edu> voss@cs.uiuc.edu (Bill Voss;;;356-4652;jerry.cs.uiuc.edu) writes: >------------------------------------------------------------------------------ >First let me say thanks to all my fellow debate partners in this string, both >the posters, and the emailers. This is definitely the most fun I've had in >comp.lang.smalltalk for months. We have even managed to disagree without >slipping into a flame war, in my opinion this string is an example of USENET >at its finest. Thank you. I agree that this thread is remarkably civil and thought-provoking by USENET standards. (me) >> Suppose that I have a set of points, and that I want to sort them. >> There's no canonical sorting order for points, so I have to define >PP.R4 class "Point" method category "comparing" method "<= aPoint" method >comment says "Answer whether the receiver is 'neither below nor to the right' >of aPoint." So you can stick PP.R4 Point's directly into a sorted collection. This is not a total order. Try (0 @ 3) <= (3 @ 0) ==> false (3 @ 0) <= (0 @ 3) ==> false You can put them into a sorted collection, and you will get an answer back, but it isn't really sorted. For example, you get. (Array with: (3@0) with: (0@3)) asSortedCollection ==> SortedCollection (0@3 3@0 ) (Array with: (0@3) with: (3@0)) asSortedCollection ==> SortedCollection (3@0 0@3 ) (This is also in PP.R4, but is not likely specific to that version). > >If you need a different canonical sorting order, then yes I most definitely >would suggest adding a simple method "xyzCompare:" to class point. Then use >the simple and readable sort block [:p1 :p2 |p1 xyzCompare: p2]. Both methods >will obviously work, but there is an implicit difference in encapsulation. > I will agree that there is no particular harm done by using xyzCompare: p2, but I don't think it gains you very much over the other approach. It might be better style though, so I'll concede you this one. >> [ :point1 :point2 | point1 x <= point2 x] >Would you use that sort block if you KNEW that the internal representation of >a Point was NOT based on instance variables X and Y? I like my code to run as If I wanted to sort by x-coordinate I don't see that I would have any choice. >I`ld also either name xyzCompare: something like > orderByGodzilianSpiral: >in this case I've already written my GodzilianSpiral code when I need it again >OR > projectHack12BSorter: >in which case WHEN I later decide I didn't really need that infinite precision >asymptotically corrected aglutinated comparison, and could instead get by with >an 8 way truncated fast shuffle I only need to change code in ONE PLACE. The code for sorting should already be in one place. The sort block does not implement a sorting routine, just a comparison, and in the instances where the comparison was trivially based on a property of the object which could be considered a key (whether this is part of the representation or not). In the case of an even moderately complex comparison function I would definitely put it in a method. e.g. aPoint leqInClockwiseRadialOrderWithOrigin: anOriginPoint than: anotherPoint Although, with regard to the efficiency you like (which I think I just deleted) for an ordering by key which is expensive it is considerably more efficient to calculate the property once at the beginning and then compare keys. e.g. allPoints do: [:eachPoint | eachPoint associateProperty: (eachPoint GodzilianSpiralNumberWRT: anOrigin)]. (Pretend points have property lists hanging of them. One could also use a dictionary or some other mechanism to do this). This efficiency, however, breaks encapsulation somewhat in that you need to use a different sorting routine depending on how expensive your ordering is. >I've never done any finite element analysis, I don't think I could even tell >you what it is used for. (My, my, the state of my education.... ;-) Doing physical calculations about objects. e.g. to analyze heat flow in a weld, break the object being welded into a number (often large) of regularly shaped elements, and then evaluate your heat functions at the corners of these objects discretely rather than continuously throughout the object. > >That said, it sounds as though you are treating Points as simple Records here. >Extracting out the raw data they contain, and plugging that data into a >mathematical formula. There is nothing apriori wrong with doing this. In >such a situation the difficult portion is usually the mathematics, not the >programming. However, I wouldn't describe it as object oriented programming >either, it is programming in an imperative style using an object oriented >language. I know if someone gave me a problem which involved complex >mathematical formulas I'ld do the same thing. I'ld simply plug the formulas >into my program. Not all problems require a hammer, and not all problems >require object oriented programming. This seems to be one of those cases >where imperative is better, and imperative programming definitely DOES work >better with access methods. Good analysis of the situation. In fact, IMHO, a lot of the difference in style between OOP and non-OOP programming is in the difference in difficulty between different parts of the program. Imperative programming assumes that the difficult parts are the functions, and that data is some other stuff that functions work on. The important mental shift in OOP is that usually the data are more important, and that they should be treated as primary with functions dependent on them. However, this is not true in all domains, and certain kinds of mathematics are examples of this. For example, there are a great many integer functions in math (factorial, catalan, fibonacci, etc. etc.). The natural impulse is to add these as methods in class Integer. However, these can be very expensive to evaluate, so it's probably worth caching them, or intermediate results (e.g. for catalan it's a big win to cache intermediate C(n,k) values). These could be added as class variables in class Integer, but pretty soon this gets awfully messy. What's happening is that the mathematical functions and their associated data are becoming "heavier" than the functions strictly associated with single integers, and IMHO the correct approach is to split these functions out into a separate class or classes. This sounds a bit like what you are proposing in your P.S. i.e. >P.S. >Thinking about it a wee bit more, perhaps you could handle a mathematical >formula in a rather object oriented style by making the formula itself a >class. > >So I need to update my exceptions list. > > Using ACCESS methods is almost always undesirable EXCEPT: > 1) As class "private" methods. > 2) To encapsulate an external interface you can't easily change. > 3) To implement something not easily expressed in an object oriented style. > Such as a complex mathematical formula better expressed > in an imperative style. This sounds reasonable, although it has the potential to be dangerous given the common mentality that "object-oriented" means good and "not object-oriented" means bad. "To implement something not easily expressed in an OO style" also sounds a bit like a "To handle cases where you need to" catchall, but perhaps that's what it amounts to. Unrelated P.S. I definitely think that a Vector should be a subclass somewhere under Magnitude. In PPS, probably under ArithmeticValue, but not Number. In a number of places Smalltalk treats points like Vectors of size 2 (e.g. (3@2) * 5), but they're not well-integrated. -- -- Alan Knight knight@mrco.carleton.ca +1 613 788 5783 Support Dept. of Mechanical and Aeronautical Engineering the Carleton University, Ottawa, Ontario, Canada, K1S 5B6 LPF
kentb@argosy.UUCP (Kent Beck) (04/26/91)
Alan Knight makes a good point about operations becoming "heavier" than data. The image has a good example of this in BitBlt. If the "function" becomes so important or hairy that it doesn't seem to fit in any existing class, make it an object. Then all the benefits of objects accrue. If you thought that graphics operations should be in Form (probably as primitives) you would have locked out all the neat, nasty efficiency hacks that having BitBlt be its own object enable, like scrolling Strings in a terminal emulator. Kent
kentb@argosy.UUCP (Kent Beck) (04/26/91)
Peter Kriens says "Instance variables have a natural tendenncy (sic) to contain the most relevant information regarding an object. So it seems quite natural to me that they should be accessible outside the object." My point on accessing methods is that objects are much more about behavior than about structure. Thinking about any message send as an instance variable accessing method allows assumptions about the structure of one object to leak into others. In a perfectly disciplined world this wouldn't happen, but realistically I have seen too many cases of "highlighted: aBoolean" methods to tell beginners anything but "don't create accessing methods unless you absolutely need them, and even then think about what behavior the object might be able to offer to eliminate the need to disclose structure". The answer may be that the object cannot deliver the needed functionality itself and so it has to allow itself to be plundered from the outside, but access methods are not near the top of coding strategies. Kent
kentb@argosy.UUCP (Kent Beck) (04/27/91)
Tim Atkins writes: "Another point that has not been made is that good OO programming style asserts that instance variables should always be accessed indirectly through methods, even by methods of the same class and its subclasses." This is only true in the presence of private methods. In Smalltalk if you choose to use the above style in a class and its subclasses you cannot reasonable prevent external reliance on the same structure information. I choose to accept the occasional hassle of doing an inst var refs and adding a few "self "s rather than expose all of my instance variables to raping and plundering. Purists will say that Smalltalk should have private methods- well the one I make my living on now doesn't, so I make do with what I have. Kent
klimas@iccgcc.decnet.ab.com (05/01/91)
In article <1264@argosy.UUCP>, kentb@argosy.UUCP (Kent Beck) writes: > Tim Atkins writes: "Another point that has not been made is that good OO > programming style asserts that instance variables should always be accessed > indirectly through methods, even by methods of the same class and > its subclasses." > > This is only true in the presence of private methods. In Smalltalk if you > choose to use the above style in a class and its subclasses you cannot > reasonable prevent external reliance on the same structure information. > I choose to accept the occasional hassle of doing an inst var refs and > adding a few "self "s rather than expose all of my instance variables > to raping and plundering. Purists will say that Smalltalk should have > private methods- well the one I make my living on now doesn't, so I > make do with what I have. > > Kent Please folks, read the excellent article "Variables Limit Reusability" by Allen Wirf-Brock and Brian Wilkerson in the May/June 89 JOOP. Basically ALL instance variables (PUBLIC & PRIVATE) should be manipulated via getter and setter methods because it becomes SIGNIFICANTLY easier to do maintenance on the system later. Through actual industrial experience, this advice has proven to be exceptionally sound! BTW some claim that this practise wastes CPU resources whereas even in a tight loop. Profiling will show that the CPU overhead is miniscule when used in getter/setter methods (i.e. using anInstanceVariable ^self anInstanceVariable anInstanceVariable: aValue anInstanceVariable := aValue ).
johnson@cs.uiuc.EDU (Ralph Johnson) (05/01/91)
I bet Allan and Kent were arguing about this back in 1986 or 1987 when they worked together at Tektronix. I take Kent's side, and argue about it with Allan when I see him. The claim that all variable access should be made through messages is nice in theory, but the current Smalltalk environment gives the advantage to directly accessing variables. I generally look down my nose at anybody who chooses Smalltalk programming style on the basis of efficiency, so I pay little attention to the relative speed of direct access or message sending unless I am optimizing a program. The reasons that I like direct access are because I think it makes programs a little easier to read and it is certainly a lot easier to figure out where a variable is getting changed by doing an "inst vars" than by doing a "senders". Thus, I think that direct access to instance variables makes programs easier to maintain. I don't buy the argument that Wirfs-Brock and Wilkerson made that it is easier to eliminate variables or to replace a variable with an algorithm to compute the value when all variable accesses are made using access methods. I have never seen an instance variable that I couldn't eliminate in 5 minutes using "inst vars" and intelligent cut and pasting. Of course, this is in Smalltalk-80. Perhaps the Smalltalk-V environment changes the balance of power. My argument (which is really the same as Kent's argument--I haven't said anything new) is based on the programming environment, not the programming language. This is a classic argument that never seems to end. It must be like the argument between vi and emacs users on Unix, a matter of taste. Ralph Johnson -- University of Illinois at Urbana-Champaign
objtch@extro.ucc.su.OZ.AU (Peter Goodall) (05/14/91)
knight@mrco.carleton.ca (Alan Knight) writes: [Even more stuff about automatically generating access methods omitted] ><stuff omitted> >A nearly equivalent feature, and one which preserves a little more >encapsulation than automatically writing all acces methods is to allow >them to be written on command, one at a time. i.e. have a menu item >which writes access methods for a single instance variable. I've seen >this done in Smalltalk/V, and it could easily be done in Smalltalk-80. > > The disadvantage of this method is that it discourages comments, >particularly a problem in /V where there are no class comments. >Coming across the automatically written message >glorch: aValue > "Set the value of the instance variable glorch to aValue" > glorch := aValue. >is annoying (to say the least), when there is no indication what kind of >a thing glorch is or what it is used for. >-- >-- > Alan Knight knight@mrco.carleton.ca +1 613 788 5783 Support > Dept. of Mechanical and Aeronautical Engineering the > Carleton University, Ottawa, Ontario, Canada, K1S 5B6 LPF You can always enforce some sort of comment using a request for input. If you are automatically generating access, the method is unlikely to be complex. -- ------------------------------------------------------------------------ Peter Goodall - Smalltalk Systems Consultant - objtch@extro.ucc.su.oz.au ObjecTech Pty. Ltd. - Software Tools, Training, and Advice 162 Burns Bay Rd, LANE COVE, NSW, AUSTRALIA. - Phone/Fax: +61 2 418-7433
vernak@cateye.LABS.TEK.COM (Verna Knapp) (05/18/91)
In article <1991May1.042952.6876@m.cs.uiuc.edu> johnson@cs.uiuc.EDU (Ralph Johnson) writes: >I bet Allan and Kent were arguing about this back in 1986 or 1987 >when they worked together at Tektronix. I take Kent's side, and >argue about it with Allan when I see him. The claim that all >variable access should be made through messages is nice in theory, >but the current Smalltalk environment gives the advantage to directly >accessing variables. > (Good argument eliminated here) >Ralph Johnson -- University of Illinois at Urbana-Champaign I agree with Kent and Ralph on this one, and I have written several million bytes of Smalltalk code (Tektronix Smalltalk-80, PPS Smalltalk-80 through V2.5, and the OTI version of Smalltalk/V). At least half of this code has been maintenance and modification of existing code. I find that getter and setter methods actually slow down my understanding of existing code...I have to go an extra step or two to find out who is using them and why. I much prefer to modify code which uses direct access of variables. It is easier to see what is going on. I think this "change in only one place" mentality is actually a hangover from the days when we had to code without browsers. I found it quite useful when I was programming in assembly language. However, we have a much more powerful environment now, and that particular standard is a handicap, not an advantage. Verna Knapp vernak@crl.labs.tek.com