Dave,
I'm not sure I parsed this as you intended:
On Tue, 1 Sep 1998 16:55:11 -0400 "David N. Smith" dnsmith@watson.ibm.com wrote:
I don't think of this as a type issue, but an evaluation issue. When is the parameter evaluated: when first passed, when about to insert it into the collection (lazy evaluation), or never? I claim one needs all three.
#at:ifAbsentPut:, as implemented elsewhere, evaluates the parameter expression before the message is sent. If it is a block then the block is put into the collection.
Are you referring to actual implementations? I seem to recall from VSE work that if the second argument is a block, then it is #value'd if the key does not exist and the result is added. Perhaps this was a local implementation (I cannot recall at this point) - do different implementations exist?
#at:ifAbsentPut:, as proposed, would allow a block and evaluate its contents before putting the result into the collection. However, this prevents actually putting a block into the collection without an ugly contortion:
dict at: aKey ifAbsentPut: [ [ blockCode ] ]
It seems to me that there is a limit to this criticism: sometimes we want to insert a simple value into the dictionary, sometimes we want to to insert a simple value resulting from evaluation of a block, or we might want to insert a block obtained from evaluating a block, such as:
d at: key ifAbsentPut: [b ifTrue: [[:x | x doSomething]] ifFalse: [[:x | x doSomethingElse]]]
or even deeper levels of nested evaluation.
#at:ifAbsentPutValueOf: makes this explicit. One is not putting the block into the collection, but the result of evaluating the block.
Note that, for consistancy, if the thrid example is considered wrong, one should then make #at:put: have the same properties as #at:ifAbsentPut:. If a block is passed it is evaluated before putting its value into the collection.
I think (for internal consistency, at least) that we need to consider other #xxx:ifXxx: protocols:
e.g.: Collection>>detect:ifNone: evaluates the second argument only if no matching element is found.
FWIW:
Symbol allInstances select: [ :x | (x findString: ':if' startingAt: 1) > 0]
returns 59 Symbols in my image, and a cursory check suggests that they only evaluate the ifXxx: argument only when needed.
Personally, I would like to write:
d at: k ifAbsentPut: 0 "inserting (and returning) 0 if k is not a key in d" d at: k ifAbsentPut: aBlock "inserting (and ^) the result of evaluating <aBlock>" d at: k ifAbsentPut: [aBlock] "inserting (and ^) aBlock"
but I could easily live with:
d at: k put: [0] "to insert and return 0"
Cheers, Bob
At 20:13 -0400 9/1/98, Bob Arning wrote:
SNIP...
#at:ifAbsentPut:, as implemented elsewhere, evaluates the parameter expression before the message is sent. If it is a block then the block is put into the collection.
Are you referring to actual implementations? I seem to recall from VSE work that if the second argument is a block, then it is #value'd if the key does not exist and the result is added. Perhaps this was a local implementation (I cannot recall at this point) - do different implementations exist?
I stand corrected. Existing implementations DO seem to value the block, and oddly enough my fingers know this since I sometimes write the method. I always (well, almost always) test things before I append, but since I was so SURE...
It is true that:
aDictionary at: key put: [ 1 ]
and:
aDictionary at: key ifAbsentPut: [ 1 ]
put totally different things into the dictionary. This still feels inconsistent to me but maybe the 'if' signals the intent to evaluate (as others have pointed out).
This is an example of lazy evaluation which, if we had it all to do over, I'd continue to argue against; I'd prefer that it be more explicit.
Sigh...
Dave _______________________________ David N. Smith IBM T J Watson Research Center Hawthorne, NY _______________________________ Any opinions or recommendations herein are those of the author and not of his employer.
squeak-dev@lists.squeakfoundation.org