[comp.lang.smalltalk] Is SELF a naughty OOP construct?

weeks@hplb29a.HPL.HP.COM (Gregory Weeks) (05/28/88)

I have a suspicion that the SELF construct may turn out to be the GOTO of
object-oriented programming.  That is, it will be viewed as promoting
unsound code structure.  However, I have no arguments to back up this
suspicion.

Does anyone?

alan@pdn.UUCP (Alan Lovejoy) (05/30/88)

In article <1620001@hplb29a.HPL.HP.COM> weeks@hplb29a.HPL.HP.COM (Gregory Weeks) writes:
>I have a suspicion that the SELF construct may turn out to be the GOTO of
>object-oriented programming.  That is, it will be viewed as promoting
>unsound code structure.  However, I have no arguments to back up this
>suspicion.
>
>Does anyone?

SELF is necessary in ST80 because methods always have one unnamed
argument:  the receiver of the message.  Because this argument is
unnamed, it is always called self.  This is an irregularity which
makes abstracting over code that references SELF more difficult.

For example, consider the following method:

op: rightArg

  | temp |
  temp := rightArg foo.
  temp := temp bar: self.
  ^temp crunch

To invoke this method, one must code: leftArg op: rightArg.  Of course,
leftArg must be an object of the class in which the method op: is
defined.  If rightArg is or is supposed to be of this same class, then
one might expect to be able to code: rightArg op: leftArg.  That is,
it may be that op: is commutative (although the method as coded probably
is not commutative).  However, if rightArg is or is supposed to be of
a different class, then you cannot code rightArg op: leftArg unless
you define a method "op:" in rightArg's class.  It would be nice if
we could simply take a copy of the "op:" method we have already coded
and add it to the instance methods of rightArg's class.  Unfortunately,
that won't work, becase of the SELF construct.  Instead we must recode
"op:" as follows:

op: rightArg

  | temp |
  temp := self foo.
  temp := temp bar: rightArg.
  ^temp crunch 

Now we can code "a op: b" or "b op: a" with full commutivity in cases
where a and b are not the same class.

To summarize:  SELF inappropriately forces algorithms to be dependant
on which method argument is the special case otherwise known as the
receiver of the message.

-- 
Alan Lovejoy; alan@pdn; 813-530-8241; Paradyne Corporation: Largo, Florida.
Disclaimer: Do not confuse my views with the official views of Paradyne
            Corporation (regardless of how confusing those views may be).
Motto: Never put off to run-time what you can do at compile-time!  

shopiro@alice.UUCP (05/31/88)

> In article <1620001@hplb29a.HPL.HP.COM> weeks@hplb29a.HPL.HP.COM (Gregory Weeks) writes:
> > <Should SELF be avoided in ST-80?>
No.

In article <3313@pdn.UUCP>, alan@pdn.UUCP writes:
> 
> SELF is necessary in ST80 because methods always have one unnamed
> argument:  the receiver of the message.  Because this argument is
> unnamed, it is always called self.  This is an irregularity which
> makes abstracting over code that references SELF more difficult.
> 
<Examples deleted>

The issue is even more fundamental than you have said.  The method
belongs to a class, and the language guarantees that the receiver is
an instance of that class or one of its subclasses.  The language does
not guarantee anything about the arguments (except that they exist).
Thus, for example, the method can access the instance variables of
only the receiver.

When you write in ST-80

	leftArg op: rightArg

it means ``send the message op:  with the argument rightArg to
leftArg.''  Then the class of leftArg (only) determines what happens
next.  Sometimes you would like this expression to mean ``apply the
operator op:  to leftArg and rightArg,''  Where op: looks at both
leftArg and rightArg to determine what to do.  These concepts are
different and simulating either one with the other can be a real pain.
The most obvious example where the second meaning is desired is
arithmetic.

Making SELF into an explicit argument would not appreciably simplify
this problem, which is intrinsic to object-oriented programming.
> 
> To summarize:  SELF inappropriately forces algorithms to be dependant
> on which method argument is the special case otherwise known as the
> receiver of the message.
> 
My summary:  Object-oriented programming focuses on objects, which makes
applications that need to focus on operations difficult.

Caveat:  Focussing on objects is exactly the right approach for most
(but not all) applications.

> -- 
> Alan Lovejoy; alan@pdn; 813-530-8241; Paradyne Corporation: Largo, Florida.
> Motto: Never put off to run-time what you can do at compile-time!  
Right on!

-- 
		Jonathan Shopiro
		AT&T Bell Laboratories, Murray Hill, NJ  07974
		research!shopiro   (201) 582-4179

brianw@tekcrl.TEK.COM (Brian Wilkerson) (05/31/88)

In article <3313@pdn.UUCP> alan@pdn.UUCP (0000-Alan Lovejoy) writes:
>
> SELF is necessary in ST80 because methods always have one unnamed
> argument:  the receiver of the message.  Because this argument is
> unnamed, it is always called self.  This is an irregularity which
> makes abstracting over code that references SELF more difficult.
>
> To summarize:  SELF inappropriately forces algorithms to be dependant
> on which method argument is the special case otherwise known as the
> receiver of the message.

After thinking about this for some time, I am forced to disagree with you.
If the method #op: were commutative (which it was not in your example), the
names of the first and second parameters would not need to be reversed in
the process of copying the method to the expected class of the second
argument.  For example, the following method, which is commutative
(assuming, of course, that #bar: is commutative), would work equally well
in either the class of the first argument or the class of the second
argument, without changing a single character in the source code.

	op: rightArg

		^self foo bar: rightArg foo

The name of the (implicit) first parameter, self, is of no more consequence
than the name of the (explicit) second parameter, rightArg.  Your example
appears to support your argument only because the method you chose was not
commutative.  The form of abstraction that you claim is missing is the
ability to copy source code without change.  Being able to name the first
parameter explicitly would give you the ability to change the method header
rather than the body, but would still require modification of the source
code.  Non-commutative methods will always need to be rewritten.

> In article <1620001@hplb29a.HPL.HP.COM> weeks@hplb29a.HPL.HP.COM (Gregory Weeks) writes:
>
> I have a suspicion that the SELF construct may turn out to be the GOTO of
> object-oriented programming.  That is, it will be viewed as promoting
> unsound code structure.  However, I have no arguments to back up this
> suspicion.

Although Henry Lieberman [1] argues that 'self' is insufficient for
delegation-based systems, I have seen no convincing arguments that the use
of 'self' promotes unsound code structure.  The pseudo-variable 'self' can
be thought of as an implicit parameter defined for all methods.  Thus, the
use of 'self' is no less "sound" than the use of any other parameter. 

A much more likely candidate would be the use of 'super' in methods.
Sending a message to the pseudo-variable 'super' in Smalltalk-80 (for
anyone unfamiliar with this concept) causes the method look-up process to
begin in the superclass of the class in which the method containing the
send to 'super' was implemented.  For example, if there is a class B which
is a subclass of the class A, and there is a method #foo implemented in B
that sends the message #bar to 'super', the method for #bar will be found
by beginning the look-up in the class A. If there is a class C which is a
subclass of the class B, the look-up for the method associated with the
message #bar sent to 'super' in the method #foo will still begin in the
class A, even if the method for #bar has been overridden in the class C.

I would argue that any code sending a message to 'super' (other than the
one being overridden) should be highly suspect.  Chances seem very good
that it will reduce the amount of code that can be inherited by subclasses
by forcing them to override the method containing the send to 'super' in
order to affect an override of the message being sent to 'super'.

[1] Lieberman, Henry, "Using Prototypical Objects to Implement Shared
    Behavior in Object-Oriented Systems," OOPSLA '86 Proceedings, SIGPLAN
    Notices 21(11), November 1986, pp.  214-223.

Brian Wilkerson
Software Productivity Technologies
Tektronix, Inc.
  brianw@tekcrl.uucp

johnson@uiucdcsp.cs.uiuc.edu (06/01/88)

Alan Lovejoy wishes that it were possible to write a single operation
that would work commutitively.  In other words, he wants to write one
method that will handle both Integer + Float and Float + Integer.
The solution is double dispatching and support from the programming
environment.  What you want is

Integer + anObject
	^anObject addInteger: self

Integer addInteger: anObject
	<primitive ...>

Integer addFloat: anObject
	^anObject addInteger: self

Float + anObject
	^anObject addFloat: self

Float addInteger: anObject
	^self addFloat: (anObject asFloat)

Float addFloat: anObject
	...


Note that you have to write Float + Float, Integer + Integer, and
Float + Integer, but the system can automatically construct the
other three methods if it knows that + is commutitive.  Kurt Heeble 
and I are writing a paper about this.

The original question might have been concerned about the fact that
when you write "self" you don't know exactly what it means.  A subclass
can redefine all the messages to self, so the meaning of the code in
the superclass is hard to define.  If this is your concern, don't
worry about it.  This is a very important part of object-oriented
programming, and is necessary for abstract classes and frameworks.
In my opinion, no language without this feature can be called
object-oriented.  Of course, you could implement it without having
a distinguished receiver of the message named "self".  The Common
Lisp Object System does not have self, so self is an optional, if
useful, feature of an o-o language.

jans@tekcrl.TEK.COM (Jan Steinman) (06/01/88)

<I have a suspicion that the SELF construct may turn out to be the GOTO of 
object-oriented programming.  That is, it will be viewed as promoting unsound 
code structure.>

Quite the contrary!  When I'm reviewing code, a little flag goes up in my head 
whenever I see methods that do not refer to "self".  I then start looking for 
who is getting all the terminal messages -- if 60% of them are going to one 
object, for instance aStream, I think to myself, "Hmm.  This method really 
belongs in Stream!"

There is also a strong movement toward more use of accessing protocol as a way 
of enhancing inheritance, which would be impossible without the use of "self".  
The fact that  "self" refers to the receiver rather than the implementor makes 
it rather non-analogous to your GOTO example -- "self" more closely resembles 
the oft desired COMEFROM in its actions!

I agree fully with Brian (op cit) that "super" is a much less tidy concept, and 
also submit another more deserving of scorn than "self": "thisContext".  It is 
something one either hates or loves, depending on whether one is trying to 
understand someone else's code, or is attempting to do some tricky hackery.

:::::: Software Productivity Technologies -- Experiment Manager Project ::::::
:::::: Jan Steinman N7JDB	Box 500, MS 50-383	(w)503/627-5881 ::::::
:::::: jans@tekcrl.TEK.COM	Beaverton, OR 97077	(h)503/657-7703 ::::::

bart@reed.UUCP (Bart Massey) (06/01/88)

In article <2689@tekcrl.TEK.COM> brianw@tekcrl.UUCP (Brian Wilkerson) writes:
> I would argue that any code sending a message to 'super' (other than the
> one being overridden) should be highly suspect.  Chances seem very good
> that it will reduce the amount of code that can be inherited by subclasses
> by forcing them to override the method containing the send to 'super' in
> order to affect an override of the message being sent to 'super'.

Agreed.  But the special case you mention -- invoking the "old" behavior of
a method to do most of the work -- covers most of the usages of super that
I've seen in actual code.

Perhaps what is wanted is something more restricted.  Something like Object
Pascal's "inherited" operator.  Imagine that e.g.

	newOn: bletch
		^ (overridden newOn: bletch) setSubclassInstVar: nil
		
is legal, but

	newOn: bletch
		^ (overridden new on: bletch) setSubclassInstVar: nil

is not.

I guess the semantics of the "overridden" pseudo-object are that it responds
to at most one message -- the selector and method for this message being the
selector and method overridden by the current method definition.  Given this
psuedo-object, I think I'd happily sacrifice the "super" pseudo-object.  Am
I wrong?  Am I missing something important?  Am I totally confused?  Only
my hairdresser knows for sure...

							Bart

alan@pdn.UUCP (Alan Lovejoy) (06/01/88)

In article <7935@alice.UUCP> shopiro@alice.UUCP writes:
/> In article <1620001@hplb29a.HPL.HP.COM> weeks@hplb29a.HPL.HP.COM (Gregory Weeks) writes:
/> > <Should SELF be avoided in ST-80?>
/No.
/In article <3313@pdn.UUCP>, alan@pdn.UUCP writes:
/> To summarize:  SELF inappropriately forces algorithms to be dependant
/> on which method argument is the special case otherwise known as the
/> receiver of the message.
/> 
/My summary:  Object-oriented programming focuses on objects, which makes
/applications that need to focus on operations difficult.
/
/Caveat:  Focussing on objects is exactly the right approach for most
/(but not all) applications.
/		Jonathan Shopiro
/		AT&T Bell Laboratories, Murray Hill, NJ  07974
/		research!shopiro   (201) 582-4179

You confuse the idiosyncraices if ST80 with generic obejct-oriented
programming.  Specifically, why should it be a tenet of OOP that

  leftArg op: rightArg

be interpreted as sending the message "op:" to leftArg?  Why not to
rightArg?  Why not to both?  It is certainly possible to do method
lookup based on the class of more than one object.  Future OOPLs 
probably should and probably will.

-- 
Alan Lovejoy; alan@pdn; 813-530-8241; Paradyne Corporation: Largo, Florida.
Disclaimer: Do not confuse my views with the official views of Paradyne
            Corporation (regardless of how confusing those views may be).
Motto: Never put off to run-time what you can do at compile-time!  

alan@pdn.UUCP (Alan Lovejoy) (06/01/88)

In article <2689@tekcrl.TEK.COM> brianw@tekcrl.UUCP (Brian Wilkerson) writes:
>In article <3313@pdn.UUCP> alan@pdn.UUCP (0000-Alan Lovejoy) writes:
>> To summarize:  SELF inappropriately forces algorithms to be dependant
>> on which method argument is the special case otherwise known as the
>> receiver of the message.

>After thinking about this for some time, I am forced to disagree with you.

>	op: rightArg
>
>		^self foo bar: rightArg foo

>The name of the (implicit) first parameter, self, is of no more consequence
>than the name of the (explicit) second parameter, rightArg.  Your example
>appears to support your argument only because the method you chose was not
>commutative.  The form of abstraction that you claim is missing is the
>ability to copy source code without change.  Being able to name the first
>parameter explicitly would give you the ability to change the method header
>rather than the body, but would still require modification of the source
>code.  Non-commutative methods will always need to be rewritten.

Consider a less abstract case:

In class Collection (or a sublclass):

at: aKey insert: anObject

  ^self at: (self insertKey: aKey) put: anObject

In class Object (or a subclass):

insertAt: aKey into: aCollection    

  ^aCollection at: (aCollection insertKey: aKey) put: self

Thus one may code either "collection at: key insert: thing" or
"thing insertAt: key into: collection".

The only reason the source code for these methods cannot be identical
is because of the "self" syntax, and because the definition of the
message selector heading which defines the methods is different by
design.

Source code always has to be modified in order to create new
abstractions.  But it is bad if the code that must be modified
is the body of the abstraction, instead of or in addition to any
encapsulating code which packages the abstraction.
-- 
Alan Lovejoy; alan@pdn; 813-530-8241; Paradyne Corporation: Largo, Florida.
Disclaimer: Do not confuse my views with the official views of Paradyne
            Corporation (regardless of how confusing those views may be).
Motto: Never put off to run-time what you can do at compile-time!  

alan@pdn.UUCP (Alan Lovejoy) (06/02/88)

In article <80500035@uiucdcsp> johnson@uiucdcsp.cs.uiuc.edu writes:
>
>Alan Lovejoy wishes that it were possible to write a single operation
>that would work commutitively.  In other words, he wants to write one

The point I'm trying to make about "self" has nothing to do with
commutivity.  Unfortunately, my earlier posting appears to have misled
everyone on this point.  I apologize.

So what is my point?  Simply this:  "self" is a special case.  The rules
regarding its use are different than those of other variables, and it
therefore inhibits polymorphism.

That's it.  I rest my case.


-- 
Alan Lovejoy; alan@pdn; 813-530-8241; Paradyne Corporation: Largo, Florida.
Disclaimer: Do not confuse my views with the official views of Paradyne
            Corporation (regardless of how confusing those views may be).
Motto: Never put off to run-time what you can do at compile-time!  

mario@mucs.UX.CS.MAN.AC.UK (Mario Wolczko) (06/02/88)

In article <1620001@hplb29a.HPL.HP.COM> weeks@hplb29a.HPL.HP.COM (Gregory Weeks) writes:
>I have a suspicion that the SELF construct may turn out to be the GOTO of
>object-oriented programming.  That is, it will be viewed as promoting
>unsound code structure.  However, I have no arguments to back up this
>suspicion.
>
>Does anyone?

Take a look at this paper:

	Henry Lieberman,
	Using Prototypical Objects to Implement
	Shared Behavior in Object Oriented Systems,
	Proc. OOPSLA 86, SIGPLAN Notices, 21:11, 214-223.

Lieberman argues that the use of SELF in "classical" OO systems is a
flaw, and argues that delegation is a better way to do things. 

Mario Wolczko

   ______      Dept. of Computer Science    Internet:   mario%ux.cs.man.ac.uk
 /~      ~\    The University               USENET: mcvax!ukc!man.cs.ux!mario
(    __    )   Manchester M13 9PL           JANET:      mario@uk.ac.man.cs.ux
 `-':  :`-'    U.K.                         Tel:    +44-61-275 2000 extn 6146
____;  ;_____________the mushroom project____________________________________

goldfain@osiris.cso.uiuc.edu (06/04/88)

Of course it's petty to correct spelling in notes, but I'm wondering how many
new and different spellings of "commutativity" will appear before this
note string is over ...  :-)

jorge@hpfclp.SDE.HP.COM (Jorge Gautier) (06/08/88)

> Lieberman argues that the use of SELF in "classical" OO systems is a
> flaw, and argues that delegation is a better way to do things. 

And see Lynn Stein, "Delegation is Inheritance", OOPSLA '87 for a proof
of the equivalence of delegation and (real) inheritance.

Jorge