Abstract state (was "magic" instance variables (was: Comparing syntaxes (was: What Language...))) 
Author Message
 Abstract state (was "magic" instance variables (was: Comparing syntaxes (was: What Language...)))


Quote:


> > :    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:

Quote:
>    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.
...

read more »



Wed, 30 Apr 1997 00:15:27 GMT  
 Abstract state (was "magic" instance variables (was: Comparing syntaxes (was: What Language...)))

Really superb article Jim!  I found my head nodding in enthusiastic
agreement while I read it.  :-)

    JF> This leads me to view attribute access (i.e. the dot operator)
    JF> as a manifestation of the abstract interface of a class,
    JF> rather than as a way to access the implementation.

Yes!  I think the other view is largely an outgrowth of a C++ bias.
That's why you almost never see public data members in (IMHO
well-designed, given the language :-) C++ classes.  Using dot-op on
data members breaks encapsulation.  But its always struck me as
bogusly redundant to have to specify separate accessor and mutator
methods in my _interface_ to represent a single abstract property.

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

Orthogonally, you can classify actions on objects as accessors and
mutators.  Setting properties using dot notation is an implicit
mutation, and it makes sense that when such an action changes the
internal state of an object (e.g. its background color variable), this
change is immediately progated to the external manifestation (e.g. you
see the window's color on the screen change).

Likewise, getting a property value is obviously accessing, although, a
common idiom for objects which cache expensive-to-calculate state is
that methods which appear to only access, actually do mutate some
internal cache state.  The hiding of this below the abstract interface
is a good thing if you view the caching as an irrelevent
implementation detail (which could likely change).  This is exactly
the view a client of the class should take.

    JF> The user does not (and should not) care (in general) whether
    JF> specific variables are modified.

Right, and the argument that things like de{*filter*}s are concerned with
such gritty details doesn't mean that they should be visible in the
language to ordinary coders.  De{*filter*}s are special cases (perhaps
there are others?).

    JF> They may care how long the modification takes, but this is a
    JF> different issue.

But related, because if the abstract interface encapsulates setting
and getting properties, then the implementation can change without
ripple effects.  This is, of course, from the POV of a class client.
Once you start profiling and enhancing performance, you can put on the
class provider hat, which means you might have to submerge below the
abstraction, but that's okay, because you (the programmer) are playing
a different role.

    JF> Some might suggest (and have suggested) that the dot syntax
    JF> should only be used for accessing the actual data of an object
    JF> and that functions should be used when computation is
    JF> performed.  In my opinion, this would be truly horrible, as
    JF> then the implementation is exposed and encapsulation is
    JF> broken. (The word sacrilege comes to mind. ;-)

I couldn't agree more!

-Barry



Sun, 04 May 1997 02:16:03 GMT  
 
 [ 3 post ] 

 Relevant Pages 

1. Abstract state (was "magic" instance variables (was: Comparing syntaxes (was: What Language...)))

2. Abstract state (was "magic" instance variables (was: Comparing syntaxes (was: What Language...)))

3. Abstract state (was "magic" instance variables (was: Comparing syntaxes (was: What Language...)))

4. Abstract state (was "magic" instance variables...)

5. Abstract state (was "magic" instance variables...)

6. Abstract state (was "magic" instance variables...)

7. Abstract state (was "magic" instance variables...)

8. variable "arrays" of instances

9. I am not deaf, but am I mute?

10. Deep Magic: "Uncommon Lisp"

11. Deep Magic: "Uncommon Lisp"

12. "Magic" asm sequences

 

 
Powered by phpBB® Forum Software