2011/10/5 Nicolas Cellier <[email protected]>:
> 2011/10/5 Michael Roberts <[email protected]>:
>> I find the thread a bit confused in the sense
>>
>> Dictionary *new* at: x ifAbsentPut: y
>>
>> does not make sense (is academic), because the new dictionary will
>> never have x as a key. So why profile it and use that as reasoning?...
>>
>> whereas
>>
>> myDict at:x ifAbsentPut: y
>>
>> is more interesting.
>>
>
> I guess in some examples, like when you want to serialize an object
> graph with as many nodes as branches (no or few sharing), then the
> case of absence is dominating.
> Knowing that Mariano is working on Fuel, i guess that could have
> biased his judgement.
>
>> In my experience i am biased by the use of the message in the systems
>> that already use it heavily...however it always feels that the use of
>> ifAbsentXX is intentionally deferring some code because it expresses
>> exceptional cases in the application logic. Especially if the block
>> makes an object you don't want to be doing that every time and
>> discarding the result irrespective of the presence of the key. It does
>> not have to be expensive to make, just not good style near any kind of
>> loop.
>>
>> I know some people that actually don't like the ifAbsentPut: []
>> variant. and prefer to be explicit, of the form
>>
>> (myDict includesKey: x) ifFalse: [
>>   myDict at: x put: y]
>>
>
> Both codes are valid. But note that at:ifAbsentPut: will return the
> value, or the newly put value.
> So the exact equivalent is ((myDict includesKey: x)
>    ifTrue: [myDict at: x]
>    ifFalse: [myDict at: x put: y])
>
> However, maybe Mariano is searching for tight inner loop optimization
> (we can only guess, because he didn't tell, he should have).
> In this case, using ifFalse: is good: because it is inlined by
> Compiler, it avoids a BlockClosure creation.
> But above code will cost two lookup in both branch, so it will be
> pretty bad too, depending on hash evaluation cost and collision rate,
> in a majority of cases, worse than block creation time.
> I would suggest to Mariano to implement in the Dictionary class of his
> choice the single lookup, block free creation code:
>
> at: key ifAbsentPutValue: aValue
>        "Answer the value associated with the key.
>        if key isn't found, associate it first with aValue."
>
>        | index |
>        ^((array at: (index := self scanFor: key))
>                ifNil: [ array at: index put: (key -> aValue). aValue ]
>                ifNotNil: [:value | value]
>

And please, correct me, above code is plain false :)

> But, please, please, Mariano, don't change #at:ifAbsentPut:
> As other said, conditional execution is useful both for speed and
> because in Object world, the state can change.
>        aBagOfKeys do: [:key | myDict at: key ifAbsentPut: (myStream next)].
>        aBagOfKeys do: [:key | myDict at: key ifAbsentPut: [myStream next]].
> won't behave the same if aBagOfKeys has duplicates, will they ?
>
> Last thing, #at:ifAbsentPut: is absolutely decoupled from
> Object>>#value, it does rely on Object>>#value
> So don't throw #at:ifAbsentPut: because you don't like Object>>#value.
> If you don't like it, don't use it, use a BlockClosure instead.
>
> Anyway, thanks to Mariano for these questions, they were biased, but
> not silly, no question is :)
>
> Nicolas
>
>> where y is either a simple value or some expression.
>>
>> here the test on the key has the same effect as expecting a block in
>> the inline ifAbsentPut: []  . It makes both forms basically the same,
>> with one being more compact if you prefer that. If ifAbsentPut: did
>> not expect the block then i think it would be a bit odd because you
>> would have to unroll the code in order to achieve the deferred case
>> which i feel is more common.
>>
>> cheers,
>> Mike
>>
>> On Tue, Oct 4, 2011 at 2:44 PM, Henrik Sperre Johansen
>> <[email protected]> wrote:
>>> On 04.10.2011 12:52, Mariano Martinez Peck wrote:
>>>
>>> Hi guys. If I tell you the selector is Dictionary >> #at:ifAbsentPut:  what
>>> would you expect the second parameter to be?  the value.
>>> So, one would do:
>>> Dictionary new at: #foo ifAbsentPut: 4
>>>
>>> But if you see Dictionary >>
>>>
>>> at: key ifAbsentPut: aBlock
>>>     "Return the value at the given key.
>>>     If key is not included in the receiver store the result
>>>     of evaluating aBlock as new value."
>>>
>>>     ^ self at: key ifAbsent: [self at: key put: aBlock value]
>>>
>>> so it expects a Block. Ok, we are in Smalltalk, so implementing #value is
>>> enough.
>>>
>>> Well..the previous example works, but only because we have an ugly Object >>
>>> value  that returns self.
>>> If I put instances of subclasses from ProtoObjects (proxies), that do not
>>> work anymore.
>>>
>>> So...my question is we do Dictionary at: #foo put: 4, why #at:ifAbsentPut:
>>> expects a block and not directly the value?
>>>
>>> Sven's reasons are correct I think, it's for efficiency, and elegant lazy
>>> initialization of a cache.
>>> The same reasons will never hold true when using #at:put:.
>>>
>>> in which case I need a block instead of the value object directly ?
>>>
>>> In addition to when you want delayed computation, when you have value
>>> objects who redefine/do not define #value ;)
>>>
>>> Cheers,
>>> Henry
>>>
>>
>>
>

Reply via email to