[squeak-dev] The Inbox: Collections-nice.869.mcz

Nicolas Cellier nicolas.cellier.aka.nice at gmail.com
Wed Jan 1 20:55:11 UTC 2020

Le mer. 1 janv. 2020 à 21:02, Jakob Reschke <forums.jakob at resfarm.de> a
écrit :

> Am Mi., 1. Jan. 2020 um 20:44 Uhr schrieb Nicolas Cellier <
> nicolas.cellier.aka.nice at gmail.com>:
>> Yes, indeed, this is a slight shift of semantics.
> One could also call it not implementing the interface properly or breaking
> the contract ;-)
>> But RunArray are essentially unused (just to store Text attributes).
>> They make sense only for optimization.
>> So they can have their own semantic, bended to their own purpose.
> Disregarding interfaces and contracts, yes. But it may surprise someone in
> the future.
>> It's just that we need to comment a bit more those methods.
> I brought this up during a naming discussion in VMMaker some time ago:
> some people don't read the documentation and just rely on their
> expectations. It might be foolish to do so, but expectations can be
> justified and allow you to work more efficiently. I wouldn't like to
> re-check the implementation of #collect: for every new Collection subclass
> that comes along, in particular if I don't want to look it its
> implementation.
>> If we don't want this behavior, then we probably don't want to use a
>> RunArray
> Sometimes you don't know what kind of collection you get to #collect: from.
>> Introducing a different selector would mean implementing it in other
>> collection too.
> Not necessarily. Set also does not understand #before:, you must use a
> SequenceableCollection if you want it. Conversely if you knowingly use a
> RunArray (as in Text) and you want to exploit the optimization for the
> iteration blocks, you could use the new selector(s).
> In my case, the client class had no idea whether a RunArray would be used
as storage or not.
So that would mean either a beautiful isSomething query, or a common
selector for all the possible storage class.

I could live with practically no side effects in those blocks.
It's just a matter of thinking at a different level, not at implementation
When we abuse those side effects, we let the implementation leaking.
It's like we want to do: and collect: in the same loop.
The proper way to write the example given by Christoph would be something

    (1 to: runArray size) with: runArray collect: [:i :v | i].

Using a block with side effects just is IMPLEMENTATION defined.
For example, if you do that with an Unordered collection, you'll get a
different result too with #collect:as: and (#collect:)#as: too.
That's not unexpected. Enumeration order is implementation defined anyway.
Even if Ordered, nothing prevents a BTree implementation to use a different
enumeration order too.

IMO, the fact that current phrasing explicitely tells about iterating on
each elements does not have to be taken too literally.
It means that the block argument is applied on a single element at a time.

Try writing the comment without that contract in mind:
    "Answer a collection like the receiver whose elements are transformed
using aBlock"

It's not super accurate.
If we want to be more accurate and express that aBlock will be evaluated
with receiver elements, one at a time, we end up with describing the
implementation rather than the semantics:
    "Evaluate the block with each element of the receiver as argument"

Indeed, explaining how is often a way to make understand the upper level
This get over-interpreted, and we have put too much expectations!
IMO, it was not the spirit of the original contract :)

IMO we have to unlearn some of our biased expectations (and of course this
includes myself), we have been abusing those side effects for too long.

Anyway, thanks for raising this up, that's exactly what I wanted to discuss.
If we're not ready yet, this can wait.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.squeakfoundation.org/pipermail/squeak-dev/attachments/20200101/8582dd4b/attachment.html>

More information about the Squeak-dev mailing list