<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On Fri, Nov 28, 2014 at 7:42 PM, Ben Coman <span dir="ltr"><<a href="mailto:btc@openinworld.com" target="_blank">btc@openinworld.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">Levente Uzonyi wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
The problem with changing #species is that it's overused. There are at least three different things it's used for in the Collection hierarchy:<br>
- the class of the object returned by #collect: (Set's subclasses are the exception since 2000 or so, except for WeakSet, but that's really strange, and should revisited)<br>
- the class of the object returned by #select: and friends<br>
- the class used for comparison of collections<br>
</blockquote>
<br></span>
So should there be #collectSpecies ?<br></blockquote><div><br></div><div>No. That's what species was invented for in the first place.<br> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
Which defaults to "^self species" and is overidden as required.<br>
<br>
In the case of Sets, you start with an "unordered collection without duplicates", but since #collect can introduce duplicates, you end up with "an unordered collection with duplicates" --> Bag (per Dave's suggestion)<br>
<br>
So I think its reasonable that the following two snippets should produce the same result.<br>
<br>
oc := OrderedCollection newFrom: #( 1 2 3 4 5 ).<br>
(oc collect: [ :e | e even ]) occurrencesOf: true. "--> 2"<br>
<br>
s := Set newFrom: #( 1 2 3 4 5 ).<br>
(s collect: [ :e | e even ]) occurrencesOf: true. "--> 2"<br>
<br>
<br>
which you get with the following change...<br>
<br>
Set>>collectSpecies<br>
^Bag<br>
<br>
Set>>collect: aBlock<br>
| result |<br>
result := self collectSpecies new: self size.<br>
array do: [:each | each ifNotNil: [result add: (aBlock value: each enclosedSetElement)]].<br>
^ result<span class=""><br>
<br>
<br>
<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
HashedCollection >> #select: uses #species. Changing IdentitySet<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
#species to return a Set would break #select:, because a ~~ b doesn't <br>
</blockquote></blockquote>
imply a ~= b. IdentitySet is the optimal class for the return value of IdentitySet >> #select:, because it's guaranteed to be able to hold all selected values. I think the same applies to all other kind of sets (and collections, because #select: is simply reducing the number of elements).<br>
<br>
This is not the first time this discussion comes up. There was one in 2003[1], but the thread is hard to follow. And there was one in 2009[2].<br>
I think the conclusion was that it's better to leave the method as it is, because there's no better way to do it.<br>
Sure, we could use #species in #collect:, but it would break quite a lot of stuff. For example:<br>
<br>
| k |<br>
k := KeyedSet keyBlock: [ :each | each first ].<br>
k add: #[1]; add: #[2].<br>
k collect: [ :each | each size ]<br>
</blockquote>
<br>
<br></span>
That works with the above change to produce --> a Bag(1 1)<span class=""><br>
<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
Currently this snippet returns "a Set(1)". Using #species in #collect: (via #copyEmpty) one would get an error, because SmallInteger does not understand #first. Changing the #species of KeyedSet would break #select:.<br>
<br>
So IMHO when you #collect: from a set, you should always use #collect:as:. Here is a (partial) list of methods which send #collect: to a Set:<br>
<br>
Categorizer >> #changeFromCategorySpecs:<br>
ClassFactoryForTestCase >> #createdClassNames<br>
Dictionary >> #unreferencedKeys<br>
MCDependencySorterTest >> #assertItems:orderAs:<u></u>withRequired:toLoad:<u></u>extraProvisions:<br>
MessageNode >> #analyseTempsWithin:rootNode:<u></u>assignmentPools:<br>
MethodNode >> #<u></u>referencedValuesWithinBlockExt<u></u>ent:<br>
SetTest >> #testCollect<br>
SetWithNilTest >> #runSetWithNilTestOf:<br>
<br>
There are probably many more, but we should fix all of them. A static analyzer could help a lot.<br>
<br>
Levente<br>
<br>
[1] <a href="http://lists.squeakfoundation.org/pipermail/squeak-dev/2003-February/052659.html" target="_blank">http://lists.squeakfoundation.<u></u>org/pipermail/squeak-dev/2003-<u></u>February/052659.html</a> <br>
[2] <a href="http://lists.squeakfoundation.org/pipermail/squeak-dev/2009-November/141016.html" target="_blank">http://lists.squeakfoundation.<u></u>org/pipermail/squeak-dev/2009-<u></u>November/141016.html</a> <br>
<br>
<br>
<br>
On Wed, 26 Nov 2014, David T. Lewis wrote:<br>
<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
It seems to me that Object>>species is intended to handle this sort of issue.<br>
<br>
For IdentitySet, it answers what Eliot was expecting:<br>
<br>
IdentitySet new species ==> IdentitySet<br>
Set new species ==> Set<br>
<br>
However, IdentitySet>>collect: does not make use of this, and it answers<br>
a Set for the reasons that Levente explained.<br>
<br>
Answering an Array or and OrderedCollection would not really make sense,<br>
because sets are unordered collections (but Bag might be better).<br>
</blockquote></blockquote>
<br></span>
Bag is better.<span class=""><br>
<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
Shouldn't we have an implementation of IdentitySet>>species that answers<br>
Set (or Bag), with a method comment explaining why this is the case, and<br>
with all of the collection methods using #species to answer the right<br>
kind of result?<br>
<br>
I note that IdentitySet>>collect: answers a Set, but IdentitySet>select:<br>
sends #species and therefore answers an IdentitySet.<br>
<br>
So I think that if we want the #species of an IdentitySet to be a Set,<br>
then we should make it so, and give it a method comment to explain the<br>
rationale. And the #collect: and #select: methods should both answer a<br>
result of that #species.<br>
</blockquote></blockquote>
<br></span>
I think the species of #collect: and #select: are intrinsically different. #collect: is a transformation.<span class=""><br>
<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
Dave<br>
<br>
<br>
On Thu, Nov 27, 2014 at 01:14:30AM +0100, Levente Uzonyi wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
Your example hides the problem of ordering - what Tobias is asking about -<br>
so here's another:<br>
<br>
(IdentitySet withAll: #(1 1.0)) collect: [ :each | each class ]<br>
<br>
If IdentitySet >> #collect: were returning an Array, then what would be the<br>
answer?<br>
<br>
{ SmallInteger. Float } or { Float. SmallInteger } ?<br>
</blockquote></blockquote></blockquote>
<br></span>
--> a Bag(SmallInteger Float)<span class=""><br>
<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
If you really want to have the resulting collection have the same size,<br>
but avoid the problem with ordering, then what you really need is a Bag.<br>
</blockquote></blockquote></blockquote>
<br></span>
To me that makes the most sense.<span class=""><br>
<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
On Thu, 27 Nov 2014, Frank Lesser wrote:<br>
<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
Hi Tobias,<br>
agree, a problem of "OrderedCollection"<br>
not to break a lot of other things we could return an Array.<br>
but for me collecting has priority.<br>
Frank<br>
<br>
-----Urspr?ngliche Nachricht-----<br>
Von: <a href="mailto:squeak-dev-bounces@lists.squeakfoundation.org" target="_blank">squeak-dev-bounces@lists.<u></u>squeakfoundation.org</a><br>
[mailto:<a href="mailto:squeak-dev-bounces@lists.squeakfoundation.org" target="_blank">squeak-dev-bounces@<u></u>lists.squeakfoundation.org</a>] Im Auftrag von<br>
Tobias<br>
Pape<br>
Gesendet: Donnerstag, 27. November 2014 00:48<br>
An: The general-purpose Squeak developers list<br>
Betreff: Re: [squeak-dev] IdentitySet>>collect:<br>
<br>
<br>
On 27.11.2014, at 00:34, Frank Lesser <<a href="mailto:frank-lesser@lesser-software.com" target="_blank">frank-lesser@lesser-software.<u></u>com</a>><br>
wrote:<br>
<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
hmm, not convinced<br>
<br>
(IdentitySet withAll: #(1 1.0)) collect: [:e| e asInteger ]<br>
OrderedCollection(1 1 )<br>
<br>
in LSWVST ( one-to-one ), you collect results of evaluating a block on<br>
objects.<br>
<br>
Frank<br>
maybe I am wrong ...<br>
</blockquote>
<br>
Where would the order come from for that _Ordered_Collection?<br>
</blockquote></blockquote></blockquote></blockquote>
<br></span>
An unordered OrderedCollection --> Bag<br>
<br>
cheers -ben<br>
<br>
</blockquote></div><br><br clear="all"><div><br></div>-- <br><div class="gmail_signature">best,<div>Eliot</div></div>
</div></div>