I'd like to make a third kind of suggestion for your list. Don't mix blocks and other values. Make the protocol different. For example:
at: key ifAbsentPut: anObject self at: key ifAbsent: [ self at: key put: anObject ]
at: key ifAbsentPutValue: aBlock self at: key ifAbsent: [ self at: key put: aBlock value ]
Another effect of this suggestion is that it still allows putting blocks into dictionaries as values, while suggestions 1 and 2 do not.
Good point. I was so focused on other aspects of protocol dilution, didn't consider the other form or dilution I was introducing.
at: key ifAbsentPutValueOf: aBlock self at: key ifAbsent: [ self at: key put: aBlock value ]
Perhaps this would be an even better name.
Though this wasn't the best example to illustrate it, sometime you still want an variable to hold either anObject or aBlock that it can evaluate in the same way. If you want to prevent dilution, you shouldn't use #value. However in this case IMHO I think that the name should be #asEvaluated.
BlockContext>>#asEvaluated ^self value
Object>>#asEvaluated ^self
(I still wish the convention for Blocks wasn't #value, and was instead something like #evaluate.)
--Maurice
--------------------------------------------------------------------------- Maurice Rabb 773.281.6003 Stono Technologies, LLC Chicago, USA
On Mon, 31 Aug 1998, Maurice Rabb wrote:
I'd like to make a third kind of suggestion for your list. Don't mix blocks and other values. Make the protocol different. For example:
at: key ifAbsentPut: anObject self at: key ifAbsent: [ self at: key put: anObject ]
at: key ifAbsentPutValue: aBlock self at: key ifAbsent: [ self at: key put: aBlock value ]
Another effect of this suggestion is that it still allows putting blocks into dictionaries as values, while suggestions 1 and 2 do not.
Good point. I was so focused on other aspects of protocol dilution, didn't consider the other form or dilution I was introducing.
First off, both the above methods are incorrect since they have no return value. (a typo, I assume) How about
at: key ifAbsentPut: valueProvider ^self at: key ifAbsent: [self at: key put: valueProvider value]
Now it is clear that the second parameter is expected to provide a value. (Not necesarily a block)
I think the problem with #value is that it *is* so diluted as to be practically meaningless. What posible semantics (given today's Smalltalks) could one ascribe to the message? The best I can come up with is:
"Answer some appropriate object"
I especially don't like that in VW, the ValueModel protocol intends the #value message as being functional (as in free of side-effects) whereas the blocks use value primarily in there imperative usage.
Yet, somehow, like static typing, it never seems to be a problem, in practice,
As long as were suggesting alternate selector names for Smalltalk-2002 how about #invoke, #invokeWith:, #invokeWith:with:
Though this wasn't the best example to illustrate it, sometime you still want an variable to hold either anObject or aBlock that it can evaluate in the same way. If you want to prevent dilution, you shouldn't use #value. However in this case IMHO I think that the name should be #asEvaluated.
BlockContext>>#asEvaluated ^self value
Object>>#asEvaluated ^self
(I still wish the convention for Blocks wasn't #value, and was instead something like #evaluate.)
--Maurice
How about
Object >> fullyEvaluated
fullyEvaluated | result lastResult | result := self. [(lastResult := result) respondsTo: #value] whileTrue: [ result := lastResult value. result = lastResult ifTrue: [^result]]. ^result
This is sort of like Mathematica's evaluation loop (without the Hold[]) [0->[3]] fullyEvaluated => 3
-- Mike Klein
squeak-dev@lists.squeakfoundation.org