[Pharo-project] Smells looking at collections

Levente Uzonyi leves at elte.hu
Mon Oct 25 04:20:02 EDT 2010


On Sun, 24 Oct 2010, Eliot Miranda wrote:

> Hi Levente,
>
>    in my experience playing with the Dan and Alan Borning's multiple
> inheritance implementation getting things to work was one thing, reasonably
> straighforward, but getting tool support right (file-out format so that
> things filed back in correctly, etc) was another, an dth ebulk of the work.
> Looking at your proposal below I see no problem getting it to work but see
> lots of problems integrating it, e.g. with Monticello, etc.  Traits is

You're right, integration is hard. It would probably be harder than the 
integration of Traits was.

> already integrated with Monticello, supported with tests, etc.  As such it
> is a much less risky or effortful proposition than a new scheme.  So with
> that in mind are there important functional benefits of your scheme's direct
> state access as compared with traits that use accessors to access state that
> each user of a trait must implement?  i.e. what are the key benefits you see
> in direct inst var access?

I see four benefits:
- a bit better performance on current VMs
- no name conflict (accessors)
- accessor methods can also be reused
- the ability to reuse the code of existing classes (this may cause 
problems)

Let's see the same example as a Trait:

Trait named: #TBidirectionalLink
 	uses: #()
 	category: 'ClassCompositionExample'

TBidirectionalLink >> unlink
 	self previous next: self next.
 	self next previous: self previous

TBidirectionalLink >> linkAfter: aBidirectionalLink
 	self previous: aBidirectionalLink.
 	self next: aBidirectionalLink next.
 	aBidirectionalLink next: self.
 	self next previous: self

And the user:

ECSelectorEntry subclass: #ODatedEntry
 	uses: TBidirectionalLink
 	instanceVariableNames: 'date next previous'
 	classVariableNames: ''
 	poolDictionaries: ''
 	category: 'Ocompletion'

ODatedEntry (and all other users of TBidirectionalLink) has to implement 
four methods (the accessors):

ODatedEntry >> next
 	^next

ODatedEntry >> next: anODatedEntry
 	next := anODatedEntry

ODatedEntry >> previous
         ^previous

ODatedEntry >> previous: anODatedEntry
         previous := anODatedEntry

This is only possible if ODatedEntry (or a superclass of it) doesn't 
implement any of these methods, otherwise the Trait can't be used.


Levente

>
> best
> Eliot
>
> 2010/10/24 Levente Uzonyi <leves at elte.hu>
>
>> On Sun, 24 Oct 2010, Stéphane Ducasse wrote:
>>
>>
>>>>>
>>>>>>  2) well, this is difficult to get the money for the butter and the
>>>>>>> butter - we are trying.
>>>>>>>
>>>>>>
>>>>>> If you can compose classes the way you can add a trait to a class now
>>>>>> with class and optional method level instance+class variable mapping, then
>>>>>> you're done. It would be a lot simpler to use it _and_ it would also be a
>>>>>> lot easier to implement it. Especially the tools part.
>>>>>>
>>>>>
>>>>> I'm interested to hear more about that.
>>>>>
>>>>
>>> so can you explain what you meant because I did not understand it.
>>>
>>
>> Okay, here is an example of my class composition idea. It works like
>> Traits, but it supports state and there's no distinction between a trait and
>> a class. Let's say I have a class named BidirectionalLink which can be used
>> as a link in a linked list (with a head element). This will be used as a
>> stateful trait. It's definition is like this:
>>
>> Object subclass: #BidirectionalLink
>>        instanceVariableNames: 'next previous'
>>        classVariableNames: ''
>>        poolDictionaries: ''
>>        category: 'ClassCompositionExample'
>>
>> It has a few methods:
>>
>> BidirectionalLink >> next
>>        ^next
>>
>> BidirectionalLink >> next: aBidirectionalLink
>>        next := aBidirectionalLink
>>
>> BidirectionalLink >> previous
>>        ^previous
>>
>> BidirectionalLink >> previous: aBidirectionalLink
>>        previous := aBidirectionalLink
>>
>> BidirectionalLink >> unlink
>>        previous next: next.
>>        next previous: previous
>>
>> BidirectionalLink >> linkAfter: aBidirectionalLink
>>        previous := aBidirectionalLink.
>>        next := aBidirectionalLink next.
>>        aBidirectionalLink next: self.
>>        next previous: self
>>
>> I have an existing class, let's call it ODatedEntry :). It has the
>> following definition:
>>
>> ECSelectorEntry subclass: #ODatedEntry
>>        instanceVariableNames: 'date'
>>        classVariableNames: ''
>>        poolDictionaries: ''
>>        category: 'Ocompletion'
>>
>> As you can see, it inherits some state and behavior from ECSelectorEntry.
>> My goal is to use the instances of this class in a linked list. So these
>> objects should implement the same protocol with the same behavior as
>> BidirectionalLink. I can't make it a subclass of BidirectionalLink, because
>> I also need the behavior and state from ECSelectorEntry and we don't have
>> multiple inheritance. So I'll compose the two classes. First I add the
>> necessary instance variables to the class. Let's call them nextEntry and
>> previousEntry:
>>
>> ECSelectorEntry subclass: #ODatedEntry
>>        instanceVariableNames: 'date nextEntry previousEntry'
>>        classVariableNames: ''
>>        poolDictionaries: ''
>>        category: 'Ocompletion'
>>
>> Then do the composition:
>>
>> ECSelectorEntry subclass: #ODatedEntry
>>        uses: BidirectionalLink
>>        instanceVariableNames: 'date nextEntry previousEntry'
>>        classVariableNames: ''
>>        poolDictionaries: ''
>>        category: 'Ocompletion'
>>
>> Now this doesn't work, because ODatedEntry doesn't have instance variables
>> named next and previous, but the methods of BidirectionalLink would like to
>> use them. Of course I could have used those names in the previous step and
>> (with a working implementation) this would just work out of the box. But the
>> example is about the instance variable mapping. Let's say ~ is the
>> composition operator which defines variable mapping for a class or a method.
>> Then I can write the following:
>>
>> ECSelectorEntry subclass: #ODatedEntry
>>        uses: BidirectionalLink ~ { #nextEntry -> #next. #previousEntry ->
>> #previous }
>>        instanceVariableNames: 'date nextEntry previousEntry'
>>        classVariableNames: ''
>>        poolDictionaries: ''
>>        category: 'Ocompletion'
>>
>> This means: take all methods from BidirectionalLink, but replace the
>> variable named next with nextEntry and previous with previousEntry.
>> So now ODatedEntry understands #next, #next:, #previous, #unlink, etc.
>>
>> This is the basic concept. There are some open questions, like:
>> - what happens when a composed method (or a method sent by a composed
>> method, etc) has a super send?
>> - will the class also get the methods of the superclasses of the "trait"?
>> - how does it work on the class side?
>> - what about class variables?
>> - what if I don't want to use all methods, just a few?
>>
>>
>>>
>>>  Tell us more. The problem we faced was
>>>>>        - offset access = you cannot reuse bytecode of a trait because
>>>>> the order of the offset can be different in each trait users
>>>>>
>>>>
>>>> If you mean that a CompiledMethod of a trait cannot be added to the
>>>> class' method dictionary, than that's not an issue. The current Trait
>>>> implementation was changed, because shared CompiledMethods caused other
>>>> problems.
>>>>
>>>
>>> no this is not what I meant
>>>
>>>  If you mean that the same bytecodes can't be used, than that's neither a
>>>> problem, because you can and should be compile the method again. Sharing
>>>> trailer bytes may cause problems.
>>>> So adding a method from a trait to a class is simply recompiling it in
>>>> the class' context.
>>>>
>>>
>>> this is what we wanted to avoid.
>>> Also because you may have to recompile all the other methods of the class
>>> hierarchy because if a trait add an instance
>>> variable then you should recompile the subclasses when a trait get added
>>> with a state in the superclass.
>>>
>>
>> My idea is that traits don't add instance variables. The user of the trait
>> maps the trait's variables to their own by name. So if a trait gets
>> a new variable, then only the trait and subclasses have to be recompiled.
>> The recompilation is postponed until a method of the trait which uses the
>> new instance variable is added to a class.
>>
>>
>>>  Instance variables should be used by name during compilation. If there's
>>>> a name collision then use the instance variable map I mentioned above.
>>>>
>>>
>>> what is that the instance variable map?
>>> take the time to write an example
>>>
>>
>> See above.
>>
>>
>> Levente
>>
>>
>>>         - initialization of instances variables at the trait level and the
>>>>> composition at the class levele
>>>>>
>>>>
>>>> You can always rename a trait's method in your class. So if the trait has
>>>> an #initialize method, then simply rename it to #initializeFooBar and send
>>>> it from the class' #initialize method.
>>>>
>>>
>>> Yes this is what the javascript implementation does but this is not that
>>> nice but may be there is no better solution.
>>>
>>> So indeed we could think about adding state.
>>>
>>>
>>>
>>>
>>>>
>>>> Levente
>>>>
>>>>
>>>>>
>>>>>>  3) again if nobody does anything and we just all cry on ourselves then
>>>>>>> nothing will happen.
>>>>>>>
>>>>>>
>>>>>> Tools are a must. No tools - no users.
>>>>>>
>>>>>
>>>>> Exact.
>>>>>
>>>>>  So for now identifying traits and learning is the way. Then we can
>>>>>>> refactor, redesign
>>>>>>>
>>>>>>
>>>>>> Well, Traits are in Squeak since 2006, IIRC they were available a few
>>>>>> years earlier. So in the last X (at least 4) years the only good candidate
>>>>>> to become a Trait was Magnitude.
>>>>>>
>>>>>
>>>>> Come on.
>>>>> I will not answer to such statement because I'm positive thinking.
>>>>>
>>>>> Stef
>>>>> _______________________________________________
>>>>> Pharo-project mailing list
>>>>> Pharo-project at lists.gforge.inria.fr
>>>>> http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project
>>>>>
>>>> _______________________________________________
>>>> Pharo-project mailing list
>>>> Pharo-project at lists.gforge.inria.fr
>>>> http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project
>>>>
>>>
>>>
>>> _______________________________________________
>>> Pharo-project mailing list
>>> Pharo-project at lists.gforge.inria.fr
>>> http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project
>>>
>>
>> _______________________________________________
>> Pharo-project mailing list
>> Pharo-project at lists.gforge.inria.fr
>> http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project
>>
>


More information about the Pharo-dev mailing list