[squeak-dev] The Inbox: Collections-nice.869.mcz
nicolas.cellier.aka.nice at gmail.com
Thu Jan 2 20:12:07 UTC 2020
Le mer. 1 janv. 2020 à 23:11, Jakob Reschke <forums.jakob at resfarm.de> a
> Am Mi., 1. Jan. 2020 um 21:55 Uhr schrieb Nicolas Cellier <
> nicolas.cellier.aka.nice at gmail.com>:
>> 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>:
>>>> 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.
> Ok but then you would still get away with
> Collection>>collectWithoutSideEffects: aBlock
> "Transform my elements using aBlock, which is only guaranteed to be
> evaluated at least once per distinct element."
> ^ self collect: aBlock "implementation detail"
> and specializing that for RunArray.
On the contrary, I would implement collectEachAndEvery: if someone
depending on side effects really insist on legacy behavior.
RunArray is unused (Text handling does not depend on such side effect) and
new definition is not going to cause any compatibility problem.
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].
> Or rather just (1 to: runArray size), but that is beside the point.
> The question is whether we have such an answer for all uses of
> collect:-with-side-effects. It is hard to answer unless we can enumerate
> all possible uses of collect:-with-side-effects. :-)
Well, we can detect block closing over outer variables and writing into
them, but that's a restrictive notion of side effects.
But YAGNI, RunArray usage is very limited as said above.
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.
> Yes, but in addition to that difference, in Christoph's example, the
> intermediary RunArray (before #as) is also wrong (or unexpected) in my
> opinion, not just the end result.
Because we are thinking implementation-wise, like the comment has taught us
That is what must be unlearnt.
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"
> Would that still include that the result collection is still of the same
> size as the receiver collection? This I consider important. And it somehow
> implies to me that the elements are transformed one by one because the
> block also only gets one element for each invocation.
Set collect: does not preserve size.
RunArray collect: does, new definition included.
The fact that it can collect a whole run at a time is just an
>> IMO, it was not the spirit of the original contract :)
> For what it's worth, the Blue Book states: "Evaluate the argument, aBlock,
> for each of the receiver's elements. Answer a new collection like that of
> the receiver containing the values returned by the block on each
> evaluation." (p. 137).
> The subsequent example, contrary to the one provided by Christoph, makes
> use of the block argument, of course: "The resulting collection is the same
> size as [the original collection]. Each of the elements of the new
> collection is the [collected property] of the corresponding elements of
> [the original collection]." (p. 138).
> The ANSI draft v1.9 states: "Answer a new collection constructed by
> gathering the results of evaluating [the block] with each element of the
> receiver. [...] For each element of the receiver, [the block] is evaluated
> with the element as the parameter." (p. 160) The refinements for subtypes
> are about Dictionary keys (p. 168), nil in hashed collections (pp. 178,
> 180), removed duplicates in Set (p. 180), and returning a different
> collection type in the cases of Interval (p. 194) and SortedCollection (p.
So I think the new RunArray implementation deviates at least from the
> standard draft or would introduce a weighty refinement.
>> 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.
> Maybe, and it sure is purer without side-effects. But in the end Smalltalk
> blocks explicitly support side-effects. So I am wary to forbid them in an
> operation as general as #collect:. Although "forbid" is the wrong word,
> since one can still use side-effects, only not rely on them to behave
> equally under all circumstances.
> We don't forbid side effects.
Just warn that using side effects on implementation defined enumeration
will result in implementation defined behavior.
I would favor the least surprising behavior rather than the most efficient
> if they contradict each other.
> I understand.
But RunArray is all about optimization. It is its very logic to evaluate
blocks once per run.
If we strictly adhere to this interpretation of method comment, detectMin:
detectMax: detect:ifNone: and count: should also iterate on each and every
Do you agree that count: hardly benefits from such definition?
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the Squeak-dev