super perform: #aSymbol

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

super perform: #aSymbol

Peter Goodall-4
I recently sent "super perform:" within an object's method and was suprised
to find that
the result was identical to "self perform:"

#perform: always seems to search for the implementor of its argument in the
current class,
rather than the superclass. Is this correct? I can see that it makes sense
if #perform: is
treated as a normal method - its never found in the local method dictionary
for application classes.
But it would make meta-stuff easier if it behaved differently when sent to
super.

I have appended a small test package to demonstrate..

Regards,

--Peter Goodall

---------------------------Package Start-------------------------
| package |
package := Package name: 'TestSuperPerform'.
package paxVersion: 0;
 basicComment: ''.


package classNames
 add: #Child;
 add: #Parent;
 add: #SuperPerformTest;
 yourself.

package binaryGlobalNames: (Set new
 yourself).

package globalAliases: (Set new
 yourself).

package allResourceNames: (Set new
 yourself).

package setPrerequisites: (IdentitySet new
 add: '..\Object Arts\Dolphin\Base\Dolphin';
 add: '..\Camp Smalltalk\SUnit\SUnit';
 yourself).

package!

"Class Definitions"!

Object subclass: #Parent
 instanceVariableNames: ''
 classVariableNames: ''
 poolDictionaries: ''
 classInstanceVariableNames: ''!
Parent subclass: #Child
 instanceVariableNames: ''
 classVariableNames: ''
 poolDictionaries: ''
 classInstanceVariableNames: ''!
TestCase subclass: #SuperPerformTest
 instanceVariableNames: ''
 classVariableNames: ''
 poolDictionaries: ''
 classInstanceVariableNames: ''!

"Global Aliases"!


"Loose Methods"!

"End of package definition"!

"Source Globals"!

"Classes"!

Parent guid: (GUID fromString: '{D68975D3-D4AB-4ABE-8B18-D62F3B560EC8}')!
Parent comment: ''!
!Parent categoriesForClass!Kernel-Objects! !
!Parent methodsFor!

answer
 ^4! !
!Parent categoriesFor: #answer!public! !

Child guid: (GUID fromString: '{8E20207D-2ABD-46BC-A31E-F6A9AB047E4C}')!
Child comment: ''!
!Child categoriesForClass!Kernel-Objects! !
!Child methodsFor!

answer
 ^3!

superExecuteAnswer
 ^super answer!

superPerformAnswer
 ^super perform: #answer! !
!Child categoriesFor: #answer!public! !
!Child categoriesFor: #superExecuteAnswer!public! !
!Child categoriesFor: #superPerformAnswer!public! !

SuperPerformTest guid: (GUID fromString:
'{5F5AD5CD-AB11-47F6-8FC4-88DBCA4A546E}')!
SuperPerformTest comment: ''!
!SuperPerformTest categoriesForClass!Unclassified! !
!SuperPerformTest methodsFor!

testNormalExecute
 | parent child |
 parent := Parent new.
 self should: [parent answer = 4].
 child := Child new.
 self should: [child answer = 3]!

testSuperExecute
 | parent child |
 parent := Parent new.
 child := Child new.
 self should: [child superExecuteAnswer = parent answer]!

testSuperPerform
 | parent child |
 parent := Parent new.
 child := Child new.
 self should: [child superPerformAnswer = parent answer]! !
!SuperPerformTest categoriesFor: #testNormalExecute!public! !
!SuperPerformTest categoriesFor: #testSuperExecute!public! !
!SuperPerformTest categoriesFor: #testSuperPerform!public! !

"Binary Globals"!

"Resources"!


Reply | Threaded
Open this post in threaded view
|

Re: super perform: #aSymbol

Chris Uppal-3
Peter Goodall wrote:

> I recently sent "super perform:" within an object's method and was
> suprised to find that the result was identical to "self perform:"

That's what I'd expect since...

> #perform: always seems to search for the implementor of its argument in
> the current class, rather than the superclass.

> I can see that it makes sense if #perform: is
> treated as a normal method

Well, it *is* a normal method.

To do something different, #perform: (or some other selector) would have to be
added to Smalltalk *syntax* and special-cased in it's semantics.  And to me
that seems like a huge increase in complexity for very little actual gain.
E.g.
you'd have to define (and users of the facility would have to remember) what
expressions like:

    super perform: #perform: withArguments: #(#size)

meant.  Would the "specialness" of the super-perform persist into the second
#perform ?

FWIW, you can define #performSuper along the lines of (untested and with no
error checking code):

    Object>>performSuper: aSelector
        | method |
        method := (((self basicClass lookupMethod: aSelector)
                                methodClass superclass)
                                    lookupMethod: aSelector.
        ^ method value: self withArguments: #().

Which is horrible, I'll grant, but is still preferable (IMO) to adding
complexity to the language syntax and semantics.

    -- chris


Reply | Threaded
Open this post in threaded view
|

Re: super perform: #aSymbol

Peter Goodall-4
Thanks Chris,

"Chris Uppal" <[hidden email]> wrote in message
news:3ff55800$1$61064$[hidden email]...

> Peter Goodall wrote:
>
> > I recently sent "super perform:" within an object's method and was
> > suprised to find that the result was identical to "self perform:"
>
> That's what I'd expect since...
>
> > #perform: always seems to search for the implementor of its argument in
> > the current class, rather than the superclass.
>
> > I can see that it makes sense if #perform: is
> > treated as a normal method
>
> Well, it *is* a normal method.

Yes, *I noticed* - it does have an real implementor. Unlike #ifTrue:
>
> To do something different, #perform: (or some other selector) would have
to be
> added to Smalltalk *syntax* and special-cased in it's semantics.  And to
me
> that seems like a huge increase in complexity for very little actual gain.

Look at the implementation of #doesNotUnderstand: in
DBAbstractRow. I am doing something similar in an abstract class, and I want
to be override
in a concrete subclass while being able to do a send to the default in super
to simplify a lot of source.
To me it is a big gain. I also think it is *behavior **not** syntax*.

> E.g.
> you'd have to define (and users of the facility would have to remember)
what
> expressions like:
>
>     super perform: #perform: withArguments: #(#size)
>
> meant.  Would the "specialness" of the super-perform persist into the
second
> #perform ?

Similar handling of specialness seems to work here -
true perform: #ifTrue: with: ['hello'] --> 'hello'
Though I confess to having never executed this expression before.

ByteCode/<primitive: 83.5> #perform: starts searching for its argument in
the superclass when its reciever
is super. Hey, wadayouknow - there's a basic implementation of #perform: in
Object.
I'm guessing - but I think all existing ordinary performs would still
work...

>
> FWIW, you can define #performSuper along the lines of (untested and with
no

> error checking code):
>
>     Object>>performSuper: aSelector
>         | method |
>         method := (((self basicClass lookupMethod: aSelector)
>                                 methodClass superclass)
>                                     lookupMethod: aSelector.
>         ^ method value: self withArguments: #().
>
> Which is horrible, I'll grant, but is still preferable (IMO) to adding
> complexity to the language syntax and semantics.
>
>     -- chris
>
>
>


Reply | Threaded
Open this post in threaded view
|

Re: super perform: #aSymbol

Peter Goodall-4
In reply to this post by Chris Uppal-3
Hi Again,

"Chris Uppal" <[hidden email]> wrote in message
news:3ff55800$1$61064$[hidden email]...
> Peter Goodall wrote:
>
> > I recently sent "super perform:" within an object's method and was
> > suprised to find that the result was identical to "self perform:"
>[...]
> FWIW, you can define #performSuper along the lines of (untested and with
no

> error checking code):
>
>     Object>>performSuper: aSelector
>         | method |
>         method := (((self basicClass lookupMethod: aSelector)
>                                 methodClass superclass)
>                                     lookupMethod: aSelector.
>         ^ method value: self withArguments: #().
>
> Which is horrible, I'll grant, but is still preferable (IMO) to adding
> complexity to the language syntax and semantics.
>
>     -- chris

I will try something like your suggestion (above), since efficiency is not
really a problem for me here.
Can you explain why use #basicClass and #methodClass rather than 'self class
superclass'?
I read that #basicClass will unhide the real class of a proxy. If I embed
the implementation of #performSuper
in my abstract application class - then I assume I don't need to solve the
entirely generic problem...


Reply | Threaded
Open this post in threaded view
|

Re: super perform: #aSymbol

Chris Uppal-3
In reply to this post by Peter Goodall-4
Peter,

> > > I can see that it makes sense if #perform: is
> > > treated as a normal method
> >
> > Well, it *is* a normal method.
>
> Yes, *I noticed* - it does have an real implementor. Unlike #ifTrue:

Well, #ifTrue: is a perfectly normal method too.  The difference is that the
compiler normally doesn't generate *sends* of #ifTrue:, but if it did then the
system would continue to work normally, just a bit slower.


> > E.g.
> > you'd have to define (and users of the facility would have to remember)
> > what expressions like:
> >
> >     super perform: #perform: withArguments: #(#size)
> >
> > meant.  Would the "specialness" of the super-perform persist into the
> > second #perform ?
>
> Similar handling of specialness seems to work here -
> true perform: #ifTrue: with: ['hello'] --> 'hello'

Yes, that's because #ifTrue: *is* a normal method.  The issue I was trying to
raise was not how to make the outer #perform work correctly when the Selector
(also #perform in this case) was "special", but what the semantics of a
super-send of #perform: would be when the Selector that was super-sent was also
#perform:


> ByteCode/<primitive: 83.5> #perform: starts searching for its argument in
> the superclass when its reciever
> is super. Hey, wadayouknow - there's a basic implementation of #perform:
> in Object.
> I'm guessing - but I think all existing ordinary performs would still
> work...

Yes, Object>>perform is there because #perform: is not part of the Smalltalk
language, it's part of the library (albeit directly supported by a VM
primitive -- presumably for performance reasons, since it doesn't have to be).
And that's my point.  It is *just* a method, nothing special.

It *might* be the case that the VM has enough information at the time when a
method is *executed* to tell that the receiver was 'super'.  OTOH it may not.
Normally 'super' only affects method lookup, not execution, so the information
may well not be available naturally.   Making #perform do something different
if it were sent to 'super' would be a major change to the semantics of
Smalltalk method lookup and execution, and as such I don't see that the gain
would be worthwhile -- especially when the desired effect can be obtained by
normal (meta-level) Smalltalk programming.

    -- chris


Reply | Threaded
Open this post in threaded view
|

Re: super perform: #aSymbol

Chris Uppal-3
In reply to this post by Peter Goodall-4
Peter,

> >     Object>>performSuper: aSelector
> >         | method |
> >         method := (((self basicClass lookupMethod: aSelector)
> >                                 methodClass superclass)
> >                                     lookupMethod: aSelector.
> >         ^ method value: self withArguments: #().
> [...]
> Can you explain why use #basicClass and #methodClass rather than 'self
> class superclass'?

#basicClass vs. #class:  pretty arbitrary choice -- I didn't really see a
strong reason to use one rather than the other.  I chose #basicClass out of a
vague feeling that meta-level programming like this "should" duck under any
overrides of #class (just as the 'super' mechanism is implemented at a lower
level than normal application programming).

The "methodClass superclass" is because you need to start the lookup in the
superclass of the class where the overriding method is found, rather than in
the immediate superclass of the receiver.  E.g. given:

    #(1 2 3) perform: #size
    #(1 2 3) performSuper: #size

The first expression will find the implementation of #size in
ArrayedCollection, so the overridden implementation (in SequencableCollection)
can only be discovered by starting above ArrayedCollection, not in the
superclass of Array.

> If I embed
> the implementation of #performSuper
> in my abstract application class - then I assume I don't need to solve the
> entirely generic problem...

I'd imagine so.  It depends on the details of what you're trying to do.  You
mentioned overriding #doesNotUnderstand:, and I'm having difficulty imagining
why you need this facility in that context -- can you explain a bit more?

Incidentally, and FWIW, you may not know that you can embed a reference to the
class where a method is defined (as opposed to the class of the receiver) by
using an expression like: ##(this) in the body of the method.

    -- chris


Reply | Threaded
Open this post in threaded view
|

Re: super perform: #aSymbol

panu-3
In reply to this post by Peter Goodall-4
Peter Goodall wrote:

> I recently sent "super perform:" within an object's method and was suprised
> to find that  the result was identical to "self perform:"
>

Point is that sending something 'super' and 'self'
have  the same effect unless the method you are
sending is defined differently in the current class
and some of its superclasses.

So in your case, to see any difference, you should
redefine #perform:, not the method-selector that is
passed as the argument to it. To me this makes perfect
sense.

To achieve the effect you are after you might define
a new method #myPerform: , and somehow have it implement
the behavior you are after.

-Panu Viljamaa