Newsgroups: comp.lang.misc,comp.lang.perl,comp.lang.scheme,comp.lang.smalltalk,comp.object,comp.lang.python
Path: cantaloupe.srv.cs.cmu.edu!das-news2.harvard.edu!news2.near.net!howland.reston.ans.net!pipex!uknet!comlab.ox.ac.uk!sable.ox.ac.uk!lady0065
From: lady0065@sable.ox.ac.uk (David J Hopwood)
Subject: Re: Abstract state (was "magic" instance variables...)
Message-ID: <1994Nov18.225459.24793@inca.comlab.ox.ac.uk>
Sender: david.hopwood@lmh.oxford.ac.uk
Organization: Oxford University Computing Service, 13 Banbury Rd, Oxford, UK
References: <JFULTON.94Nov11111529@dsjfqvarsa.er.usgs.gov>
Date: Fri, 18 Nov 94 22:54:59 GMT
Lines: 123
Xref: glinda.oz.cs.cmu.edu comp.lang.misc:19219 comp.lang.perl:39264 comp.lang.scheme:11371 comp.lang.smalltalk:18117 comp.object:22797 comp.lang.python:2727

Jim Fulton  <jfulton@dsjfqvarsa.er.usgs.GOV> wrote:
>"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 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" )

These are different questions. Jake Donham seems to be referring to code
being executed whenever a variable is _referenced_, not just on assignment to
one of its attributes.

To take an extreme example, in pure OO languages like Smalltalk and Self
(pure meaning that everything is an object), I can provide my own
implementations of primitive types. So for example, I could implement an
object which looks like an integer, but which causes a global variable to be
updated whenever a message (eg. addition) is sent to it.

If used incorrectly, this can make it very difficult to reason about programs.
Some people have argued that it is useful to separate 'functional' objects
(ie. immutable objects, similar to those used in functional programming
languages), from those which have mutable state, and that this distinction
should be part of an object's type (if the language uses static types). The
use of functional objects would be guaranteed free of side-effects.

As a convention, this is a good idea. However, it cannot be enforced by an
imperative programming language without some loss of expressive power (for
example, this would disallow cacheing of attributes that are expensive to
calculate on each call).

In general, I think that the extra flexibility allowed by executing code
whenever an attribute is accessed or assigned to, is well worth any
disadvantages caused by allowing side-effects.

Another issue is whether the representation of instance variables is exported
to subclasses. Languages which use slots (eg. Self) do not do this, and so
allow the representation of a superclass to be changed without affecting its
subclasses. This is in general a Good Thing, especially since in the common
case, the extra call can be optimized out.

Now, on to assignment. Consider the following code (not in any particular
programming language):

   type Foo
       attr y: Bar -- define an attribute.
       ...

   var x: Foo
   x := ...;
   x.y := some_expression;

Some languages, eg. Sather, Trellis/Owl (I think), and the IDL -> C/C++
mapping, will interpret this as being equivalent to:

   type Foo
       y: Bar
       set_y (Bar) -- convert an attribute to a pair of get/set methods
       ...

   var x: Foo
   x := ...;
   x.set_y (some_expression); -- convert an attribute assignment to a
                              -- method call

This is just syntactic sugar, but it is useful. It avoids any need to manually
write trivial get/set methods in classes conforming to Foo, and provides a
convenient syntax for assignment-like operations on the attributes of objects.

BUT, note that the meaning of := above when used to assign to a local variable
is different from when it is used with the set_ sugar. Ideally, two different
operators should be used (eg. := and ::= respectively). They have different
syntax and semantics:

1. the LHS of := is always a variable; the LHS of ::= is always a method
   call referring to an attribute.
2. := executes in time bounded by a small constant; ::= may be computationally
   expensive.
3. := only ever changes the variable on its LHS; ::= may cause non-local side
   effects.

Using two different operators is no less convenient; a compiler will always
be able to detect if the wrong one is used (by looking at whether the LHS
is a variable or method call), and give an intelligent error message.
::= would also be used for operations like array assignment, so that

  sequence[i] ::= value;          -- is equivalent to
  sequence.element (i) ::= value; -- which is equivalent to
  sequence.set_element (i, value);

This gives an intuitive syntax for array assignment and similar operations,
without treating each type as a special case.

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

Agreed.

David Hopwood
david.hopwood@lmh.oxford.ac.uk
