Newsgroups: comp.lang.perl,comp.lang.python,comp.lang.scheme,comp.lang.misc,comp.object,comp.lang.smalltalk
Path: cantaloupe.srv.cs.cmu.edu!das-news2.harvard.edu!news2.near.net!news.mathworks.com!news.alpha.net!uwm.edu!fnnews.fnal.gov!stc06.CTD.ORNL.GOV!rsg1.er.usgs.gov!rsg1.er.usgs.gov!jfulton
From: jfulton@dsjfqvarsa.er.usgs.GOV (Jim Fulton )
Subject: Abstract state (was "magic" instance variables (was: Comparing syntaxes (was: What Language...)))
In-Reply-To: "Steven D. Majewski"'s message of Tue, 8 Nov 1994 06:49:37 GMT
Message-ID: <JFULTON.94Nov11111529@dsjfqvarsa.er.usgs.GOV>
Sender: news@rsg1.er.usgs.gov
Organization: U.S. Geological Survey
Date: Fri, 11 Nov 1994 16:15:27 GMT
Lines: 240
Xref: glinda.oz.cs.cmu.edu comp.lang.perl:38655 comp.lang.python:2588 comp.lang.scheme:11166 comp.lang.misc:19045 comp.object:22547 comp.lang.smalltalk:17882

>>>>> "Steven" == Steven D Majewski <sdm7g@Virginia.EDU> writes:
In article <Pine.A32.3.90.941108012326.18486B-100000@elvis.med.Virginia.EDU> "Steven D. Majewski" <sdm7g@Virginia.EDU> writes:

> On 8 Nov 1994, Tom Christiansen wrote:
> 
> > :-> In comp.lang.perl, jdonham@hadron.us.oracle.com (Jake Donham) writes:
> > :    Tom> Of course, in perl, the x, i, and j variables might all
> > :    Tom> actually be tied to secret little package functions, making
> > :    Tom> it easier to implement
> > :
> > :    Tom>     print $his_host{"load average"};
> > :
> > :    Tom> in a straightforward way:the his_host hash table gets bound
> > :    Tom> to some clever little rpc fetcher or SNMP thing.
> > :
> > :Yikes. Doesn't it make you nervous that simply by referencing a
> > :variable all manner of unknown things could happen? Computationally
> > :expensive procedures could be called, global variables could be
> > :modified--this is not going to help debugging.
> > :
> > :We're used to procedures not being referentially transparent (and a
> > :lot of people aren't happy with that) but referentially opaque
> > :variables are an accident waiting to happen. This (mis-)feature has a
> > :lot of "gee-whiz" value but the potential for misuse is large.
> > :
> > :Jake
> > :
> > :(I'll admit my Scheme bigotry, but this is just one more thing to
> > :dislike about Perl.)
> > 
> > Huh?  It's phenomenally powerful.  I suppose that you dislike the fact 
> > that you can map a hash table in memory to one on disk (via dbm)?
> > That's just one example of its use.  The SNMP example hasn't to my 
> > knowledge been implemented, but it could be.
> > 
> > You could have something like:
> > 
> >     $current_nice_value = -20;
> > 
> > automatically do the right thing by making the setpriority system call on 
> > systems that support it.
> > 
>  [ ... ] 
> > I really think you're really selling this powerful method short.  It is an
> > enabling mechanism object abstraction to high degree.  Are you sure you've
> > really thought about it a lot?
> 
> We had some discussion about just this sort of idiom  at the
> NIST Python workshop. We were discussing the design of GUI APIs,
> and the pro and cons of:

>    oldbg = window.background 
>    window.background = "red" 

> vs. the more procedural looking:

>    oldbg = window.getbg()
>    window.setbg( "red" ) 

> Some of us (including a few with Smalltalk and other OO language 
> experience ) justified the first on the grounds that the OBJECT 
> whose attribute was being set was a window, and there was nothing
> "magical" about the fact that a hidden procedure was required to
> make that change in the object visible. The side effect of assigning
> "red" to the windows background attribute IS supposed to be to 
> actually change the visible attribute of a visible window. 

>   Of course, the others objected on the grounds that we were hiding
> the fact that a procedure call was necessary. 

> This appears to be a OO vs a procedural oriented POV difference.
> From the OO POV, there is nothing at all wrong with abstracting
> OUT the presence or absence of procedure calls to implement
> side effects. Assignment is just a change of state. If a prodecure
> call is required to implement that change of state, then that is
> just an implementation detail. 
> 

> I would also note that Python has other hidden procedure calls.
> "sequence[i]" may just happen to translate into a call to 
> "sequence.__getitem__( i )" , and there is now a facility 
> to cause any access to a classes instance variables trap to
> a method call.

What is really interesting to me about this thread (here and at the
Python workshop) is that no one seems to be objecting to what I
thought people would object to (no pun intended, but I like it :) in
the above discussion.  Normally, an OO zealot, like myself, would
object to something like:

>    oldbg = window.background 
>    window.background = "red" 

On the grounds that it gives the appearence of breaking encapsulation,
in that it looks like one is accessing state and therefore
implementation directly.  In fact, one of my early concerns regarding
Python was that it provided no protection of the implementation of
Python classes.  That is, any user of a class could modify or observe
the implementation of a class through data member access and
assignment.  As I dug further into Python, I found that for types
implemented in C (or C++, Fortran, etc.), attribute access is just
another method/operation of the type.  Now at version 1.1, this can be
true for Python classes as well.  This leads me to view attribute
access (i.e. the dot operator) as a manifestation of the abstract
interface of a class, rather than as a way to access the
implementation.

When designing object interfaces, it is common to separate the
interface into object properties (attributes/nouns/adjectives) and
object actions (commands/behaviors/verbs).  Properties model the
abstract state of an object.  For example, when designing object
editors, properties provide the means for users to "edit" the object.

The practice of separating an object's interface into parts that
modify abstract state and parts that perform functions can be seen in
languages, such as Smalltalk, that prevent direct access to stored
data.  In Smalltalk, it is fairly standard to use nouns for names
of methods that modify abstract properties and to use verbs (or verb
phrases) for names of methods that perform actions.

Consider a Smalltalk class for modeling two-dimensional points.  A
variety of methods might be provided for accessing or modifying a
point's abstract state:

aPoint x           "Get the euclidian horizontal position of aPoint."
aPoint x: 2        "Set same to 2."
aPoint y           "Get the euclidian vertical position of aPoint."
aPoint y: 2        "Set same to 2."
aPoint length      "Get the polar length coordinate."
aPoint length: 1   "Set the same."
aPoint angle       "Get the polar angle coordinate, in radians."
aPoint angle: 0.4  "Set the same."
aPoint degrees     "Get the polar angle coordinate, in degrees."
aPoint degrees: 20 "Set the same."

On the other hand, other methods may be provided to carry out actions:

aPoint print        "Print out some representation of aPoint."
aPoint draw         "Draw the point."
aPoint rotated: 0.1 "Return a new point has an angle that is .1 greater
                     than aPoint's."

It is fairly clear from the method names that the first group of
methods have to do with information about a point and that the second
group of methods have to do with functions that may be performed by a
point.  Of course, no model is perfect, as some methods may both
perform a function and change state, or they may change state in a
more dynamic way, for example:

aPoint rotate: 0.1 "Change the angle of a point by 0.1."

Note that you cannot tell from the first group of methods what the
implementation of a point is.  A point may be implemented with
euclidian, or polar coordinates.  Providing the ability to access and
modify the abstract state does not break encapsulation.  This ability
to hide details of the implementation while supporting the notion of
accessing state is very useful.

In Python, attribute access may be viewed as an operation that allows
a clean, easy-to-read syntax for accessing an object's abstract state.
It does not matter whether the interface to the abstract state matches
the implementation.  The user does not (and should not) care (in
general) whether specific variables are modified.  They may care how
long the modification takes, but this is a different issue.  They do
expect that the state set will be presereved in some fashion.  For
example, in the Smalltalk code:

aPoint angle: 0.5.
x := aPoint angle.

The user expects x to be bound to 0.5.  The user also does not expect
attribute access to affect unrelated objects.

One could implement a Point class for Python that supported all of the
above methods:

aPoint.x            # Get the Euclidian horizontal position of aPoint.
aPoint.x = 2        # Set same to 2.
aPoint.y            # Get the Euclidian vertical position of aPoint.
aPoint.y = 2        # Set same to 2.
aPoint.length       # Get the polar length coordinate.
aPoint.length = 1   # Set the same.
aPoint.angle        # Get the polar angle coordinate, in radians.
aPoint.angle = 0.4  # Set the same.
aPoint.degrees      # Get the polar angle coordinate, in radians.
aPoint.degrees = 20 # Set the same.

As before, one can't tell from this interface how a point is implemented.
It is clear that we are modifying abstract state.

aPoint.print()     # Print out some representation of aPoint.
aPoint.draw()      # Draw the point.
aPoint rotated(.1) # Return a new point has an angle that is .1 greate
                   # than aPoint's.
aPoint rotate(0.1) # Change the angle of a point by 0.1.

Note that a functional interface could be provided for accessing
abstract state.  For example, following the Smalltalk style:

aPoint.length()    # Get the point's length
aPoint.length(2)   # Set the point's length

or the more cluttered:

aPoint.getLength()  # Get the point's length
aPoint.setLength(2) # Set the point's length

Whether a dot-operator-based syntax or a functional syntax is used
makes no difference as far as what is being said about the
implementation (which is nothing).  I think that the dot notation is
much clearer and more easily understood, especially by a novice.

Some might suggest (and have suggested) that the dot syntax should
only be used for accessing the actual data of an object and that
functions should be used when computation is performed.  In my
opinion, this would be truly horrible, as then the implementation is
exposed and encapsulation is broken. (The word sacrilege comes
to mind. ;-)  For example, suppose I decided to implement points using
Euclidian coordinates.  I might provide an interface like:

aPoint.x            # Get the Euclidian horizontal position of aPoint.
aPoint.x = 2        # Set same to 2.
aPoint.y            # Get the Euclidian vertical position of aPoint.
aPoint.y = 2        # Set same to 2.
aPoint.length()     # Get the polar length coordinate.
aPoint.length(1)    # Set the same.
aPoint.angle()      # Get the polar angle coordinate, in radians.
aPoint.angle(0.4)   # Set the same.

Now if I want to change the implementation to use polar coordinates, I
have to change the interface completely.

Providing a simple clear syntax for modifying abstract state is a good
idea and should have nothing to do with the implementation of an
object.
--
-- Jim Fulton      jfulton@mailqvarsa.er.usgs.gov    (703) 648-5622
                   U.S. Geological Survey, Reston VA  22092 
This message is being posted to obtain or provide technical information
relating to my duties at the U.S. Geological Survey.
