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"! |
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 |
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 > > > |
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... |
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 |
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 |
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 |
Free forum by Nabble | Edit this page |