[squeak-dev] SequenceableCollection #= method in current trunk differs from Squeak 5.2

Chris Muller asqueaker at gmail.com
Fri Jul 3 02:01:29 UTC 2020


Hi Nicolas,

Yes, I understand.  For general classes, it's impossible to tolerate
non-symmetry.  This is an outward-looking tool that didn't need that
contract, and took advantage of legacy backward-compatibility via
inheritance rather than composition.  An encapsulating pointer object would
be less efficient (a bunch of extra objects + GC), and face the very same
equivalence problem due to Squeak not letting client code define
symmetrical equivalence in their subclasses.  The fault is not with
MCVersionName, it's with our core library.

Q:  What does #= have in common with #+, #-, #*, #/  ?
A:  It wants to be able to interoperate with different classes for the
*lvalue* and *rvalue*.  That's why the latter four all double-dispatch.
The same applies to #=, the only difference is its "calculation" result is
a boolean type instead of some Number.

So why can't we double-dispatch with #=, too?  Or, maybe we can simply
dispatch.  To #sameTypeAs:?

   Object>>#sameTypeAs: anObject
      "Answer whether the receiver is regarded as the same type as
anObject."
      ^ self class = anObject class

(we may need a double-dispatch, but... that's just details)

Interested in your thoughts on the hard type-checking.  Observe how
String>>#= got a pass that no one else got.  Because it was convenient, and
because we, the great developers, have full control, we softened it, let it
rely on #isString rather than the hardass draconian type-test (via an
inlined message even!).  But that reveals the limitation, *users* don't
have that luxury.  They aren't able to make anything similar for *any* subclass
and still maintain symmetric equivalence.

IMO, all this hard type-checking is crippling Squeak's wish to be more
dynamic.  Magma, too.  It could be so great if we would just *let* it.
Would you be open to double-dispatching for #= as with the other binary
operators?

Regards,
   Chris

On Wed, Jul 1, 2020 at 3:57 AM Nicolas Cellier <
nicolas.cellier.aka.nice at gmail.com> wrote:

>
>
> Le mer. 1 juil. 2020 à 06:01, Chris Muller <ma.chris.m at gmail.com> a
> écrit :
>
>> On Tue, Jun 30, 2020 at 7:55 PM Levente Uzonyi <leves at caesar.elte.hu>
>> wrote:
>>
>>> On Tue, 30 Jun 2020, Chris Muller wrote:
>>>
>>> > Might be worth reopening this discussion.  ANY dependencies on #class
>>> seem like a bad thing to me, especially while it's still an in-lined
>>> message.  Below is the MC version that did it.  Looks like the time to
>>> change this
>>>
>>> Using #class by default sounds correct to me. If some instances of
>>> classes want to be equal to each other, they should handle that
>>> themselves by overriding #=.
>>>
>>
>> Because it causes that same undesirable loss of symmetry you mentioned
>> about MCVersionName and String, except much worse because it's in a more
>> general sense instead of application-specific.  It makes headaches for
>> Magma applications because client code has to be aware of it.
>>
>> Smalltalk wants to be about messages, not types.  What do we need to do
>> to make #class non-inlined and therefore overridable?  My impression is
>> that this is on the table for 6.0, is it still?
>>
>>  - Chris
>>
>>
> Hi Chris,
> wait a minute, the failure of symmetry of MCVersionName is ABSOLUTELY
> UNRELATED to species or class.
>
> There are some wonderful hacks, there are some clever hacks, but at the
> end, you very well know that you can cheat as long as you don't get caught.
> MCVersionName is not a good hack.
>
> Here we are dressing the String with additional application specific
> semantics.
> The MCVersionName is a String which knows how to be interpreted in some
> context.
> There are lot's of contexts where we could dress strings like that,
> imagine that we create a DateString, a NumberString...
> Should we really do that?
> When we do that, we confer more responsibilities to such subclasses. They
> are a String, and also more than a String.
> But here, the application specific behavior is in contradiction with
> superclass, we redefined = with a different behavior.
> At the end, we will always get caught with schemes like that, because we
> complexify and entangle the concepts.
> It seems to me that it's a simple inheritance vs composition anti-pattern.
>
> MCVersion name hash ~= MVVersion name asString hash is a good illustration.
> It means that we're breaking a more general contract for implementing our
> hack.
> It's typical of what will happen by abuse of inheritance.
>
> Think of it, = is a relationship of equivalence. We don't want to loose
> that.
> So it has to be symmetric, but also transitive:
>     String new = StringSubclass1 & ( String new = StringSubclass2 new )
> ==> (StringSubclass1 new = StringSubclass2 new).
> That means that we would have accidental equivalence of separate domains
> if ever the string representation matches.
> That's not what we want.
>
> Also, if StringSubclass1 is more than a String, then how something less
> than a StringSubclass1 could be equivalent?
> It's not equivalent, it does not behave the same, or we would not need to
> subclass in the first place.
>
> Oh but we have String subclasses, ByteString and WideString, so what's the
> difference?
> The difference is that those are implementation details. They are
> optimized internal representations of Strings.
> We're not at all after conferring a different behavior, on the contrary.
> We absolutely want to confer the same behavior to the outside whatever the
> internal representation (class) and try to make them be really equivalent.
> Yes, more than often, the implementation details leak out of encapsulation.
> It's the problem of tradeoffs between efficiency and universality. We have
> to trade some generality for some efficiency.
>
> That's what happens with Interval and Array. They are not equivalent. They
> have slightly different behaviors and properties.
> An Interval is more than an Array (it has numeric elements with arithmetic
> progression).
> And it is less than an Array (it cannot at:put: or embed arbitrary
> objects).
> We can casually have an Array carrying the same elements as an Interval,
> which can be expressed by #hasEqualElements:
>
> species semantics was overloaded. It did also mean, in which recipient
> class should I select:, collect:...
> If we use the same recipient for collecting, then we are similar (of same
> species)... And if similar, maybe equal?
> It might be that Interval was originally seen as an implementation detail,
> a (space) optimized version of Array afterall.
> But doing so, introduced complexity.
> It entangled things.
> It broke contracts (hash), or imposed very inefficient implementation of
> hash (enumerating all elements of an Interval).
> In the end, we get caught, that is carrying more inconvenients than
> advantages when trying to sustain the illusion of equivalence.
> It's far better and simpler to just abandon the illusion, because it's
> just that, a deceitful illusion.
>
> I understand what you mean by abuse of using the class message, we have
> somehow lost a hook.
> But IMO, this hook was not useful, except for one thing proxy.
> We have more than one message in our palette, even if = is a very
> convenient symbol (short binary), we cannot dress it with different context
> specific meanings, the cheat is getting too apparent.
>
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.squeakfoundation.org/pipermail/squeak-dev/attachments/20200702/3194ecac/attachment.html>


More information about the Squeak-dev mailing list