Eliot Miranda uploaded a new version of Kernel to project The Trunk:
http://source.squeak.org/trunk/Kernel-eem.474.mcz ==================== Summary ==================== Name: Kernel-eem.474 Author: eem Time: 22 July 2010, 1:42:05.4 pm UUID: 292f2cc5-ba81-4d03-a64f-4e7cb878d68f Ancestors: Kernel-eem.473 BlockClosure>>#once Travis Griggs' neat idiom for interning values of computations. Use e.g. as in myResourceMethod ^[time-consuming computation] once =============== Diff against Kernel-eem.473 =============== Item was added: + ----- Method: CachedBlockClosure>>value (in category 'evaluating') ----- + value + ^cachedValue! Item was added: + ----- Method: BlockClosure>>becomeUncached (in category 'private') ----- + becomeUncached + "The receiver is already uncached." + ^self! Item was added: + ----- Method: CachedBlockClosure>>becomeCached (in category 'private') ----- + becomeCached + "The receiver is already cached." + ^self! Item was added: + ----- Method: BlockClosure>>becomeCached (in category 'private') ----- + becomeCached + self become: ((CachedBlockClosure new: self size) + outerContext: outerContext + startpc: startpc + numArgs: numArgs + cachedValue: self value + copiedValues: self)! Item was added: + BlockClosure variableSubclass: #CachedBlockClosure + instanceVariableNames: 'cachedValue' + classVariableNames: '' + poolDictionaries: '' + category: 'Kernel-Methods'! + + !CachedBlockClosure commentStamp: 'eem 7/22/2010 12:34' prior: 0! + I'm a BlockClosure with an added instance variable for storing the once upon a time result of evaluating myself when I was simple BlockClosure. This is triggered by sending #once to a normal BlockClosure. Future sends of once will simply return this value rather than evaluate myself. When sent value, I revert back to a BlockClosure. Originally by Travis Griggs, from whom we copy this idea with thanks. + + Instance Variables + cachedValue <Object> + + cachedValue + - result of having sent value to myself when i was just a BlockClosure! Item was added: + ----- Method: CachedBlockClosure>>once (in category 'evaluating') ----- + once + ^cachedValue! Item was added: + ----- Method: CachedBlockClosure>>outerContext:startpc:numArgs:cachedValue:copiedValues: (in category 'initialize-release') ----- + outerContext: aContext startpc: aStartpc numArgs: argCount cachedValue: aValue copiedValues: anArrayOrNil + cachedValue := aValue. + super outerContext: aContext startpc: aStartpc numArgs: argCount copiedValues: anArrayOrNil! Item was added: + ----- Method: CachedBlockClosure>>becomeUncached (in category 'private') ----- + becomeUncached + self become: (BlockClosure + outerContext: outerContext + startpc: startpc + numArgs: numArgs + copiedValues: self)! Item was added: + ----- Method: BlockClosure>>once (in category 'evaluating') ----- + once + "Answer and remember my value, answering exactly the same object in any further sends + of once or value until I become uncached. This allows one to intern values with the idiom + myResourceMethod + ^[expression] once. + The expression will be evaluated once and its result returned for any subsequent evaluations. + Originally by Travis Griggs, from whom we copy this idea with thanks." + numArgs ~= 0 ifTrue: + [self error: 'once should only be used with niladic blocks']. + self becomeCached. + ^self once! |
On 7/22/2010 8:42 PM, [hidden email] wrote:
> Eliot Miranda uploaded a new version of Kernel to project The Trunk: > http://source.squeak.org/trunk/Kernel-eem.474.mcz > > ==================== Summary ==================== > > Name: Kernel-eem.474 > Author: eem > Time: 22 July 2010, 1:42:05.4 pm > UUID: 292f2cc5-ba81-4d03-a64f-4e7cb878d68f > Ancestors: Kernel-eem.473 > > BlockClosure>>#once Travis Griggs' neat idiom for interning values of computations. Use e.g. as in > myResourceMethod > ^[time-consuming computation] once > What am I missing? Object compile: 'onceTest "Just once please" ^[Transcript cr; show: ''only once''] once'. #(1 2 3) collect: #onceTest. Cheers, - Andreas |
On Thu, Jul 22, 2010 at 5:14 PM, Andreas Raab <[hidden email]> wrote: On 7/22/2010 8:42 PM, [hidden email] wrote: ...that I'm a complete moron. The VW implementation relies on "clean blocks" where the compiler creates a literal block provided the block doesn't reference its outer environment. The current closure compiler doesn't perform the optimization so it doesn't work; a new block is being created on each invocation of the method. A test that will work is e.g.
| b | b := [Object new]. self assert: b once == b once So I need to do some work in the compiler and step back on my "Its portable" claim.
Eliot
|
On 7/22/2010 6:26 PM, Eliot Miranda wrote:
> The VW implementation relies on "clean > blocks" where the compiler creates a literal block provided the block > doesn't reference its outer environment. I see. Sounds like a hack :-) What's wrong with the straightforward: someResource ^SomeResource ifNil:[self initializeSomeResource] ? It explicitly documents that there is a shared resource by naming it which is a good thing in my understanding. Cheers, - Andreas |
On Jul 22, 2010, at 7:57 PM, Andreas Raab wrote: > On 7/22/2010 6:26 PM, Eliot Miranda wrote: >> The VW implementation relies on "clean >> blocks" where the compiler creates a literal block provided the block >> doesn't reference its outer environment. > > I see. Sounds like a hack :-) What's wrong with the straightforward: > > someResource > ^SomeResource ifNil:[self initializeSomeResource] > > ? It explicitly documents that there is a shared resource by naming > it which is a good thing in my understanding. The genesis of them (once blocks) is described in gory fireside chat detail here: http://www.cincomsmalltalk.com/userblogs/travis/blogView?showComments=true&printTitle=When_You_Come_Back&entry=3346567529 I later did a boring screencast showing a common use of them integrated into the VW IDE: http://www.cincomsmalltalk.com/userblogs/travis/blogView?showComments=true&printTitle=Assets&entry=3358066923 We actually use them quite a bit. If reading the above, what once blocks bring to the picture, is basically something similar to compile time expressions. Or at least, there's a large overlap of where a once block provides similar advantages to a CTE. There's actually some benefits to doing it with once blocks vs CTEs, for some cases. And I'm sure there's some places where CTE's would be better. -- Travis Griggs Objologist "Some people are like slinkies, not really good for much, but they can bring a smile to your face when you push them down the stairs." |
In reply to this post by Andreas.Raab
On Thu, Jul 22, 2010 at 7:57 PM, Andreas Raab <[hidden email]> wrote:
What's "wrong", or at least not as nice, is the variable and its declaration. The [expr] once form is entirely self-contained whereas the SomeResource ifNil: [self initializeResource] is accompanied with a variable declaration somewhere else. I also like (but haven't seen used) the notion of having variations on once such as onceInEverySession which can again provide the necessary void-on-startup hook without any explicit registering in a start-up list.
I do take your point about it being a hack, but its a neat hack.
best Eliot |
On 7/22/2010 10:24 PM, Eliot Miranda wrote:
> What's "wrong", or at least not as nice, is the variable and its > declaration. The [expr] once form is entirely self-contained whereas > the SomeResource ifNil: [self initializeResource] is accompanied with a > variable declaration somewhere else. I also like (but haven't seen > used) the notion of having variations on once such as onceInEverySession > which can again provide the necessary void-on-startup hook without any > explicit registering in a start-up list. I think your example wasn't very well chosen. Resource initialization is something where I very much prefer the explicit initialization approach offered by ^VarName ifNil:[...] because resources are often also something where you need to track and invalidate them and having a random method hold onto something that you need to track down from elsewhere is just unnecessarily harder than having an explicit variable that you can assign to. However, I do take Travis' point about compile time expressions. Here the whole point is that you really want the expression to be reevaluated when the defining code changes which is not easily done the above (it's possible but bothersome). > I do take your point about it being a hack, but its a neat hack. :-) Cheers, - Andreas |
Am 2010-07-23 um 07:39 schrieb Andreas Raab: > > However, I do take Travis' point about compile time expressions. Here the whole point is that you really want the expression to be reevaluated when the defining code changes which is not easily done the above (it's possible but bothersome). I think its good to point out that it is possible to differentiate between one-time-expressions and compile-time expressions. I think an expression executed once, however, not at compile time can make a pretty good lazy initializer in the sense you mentioned. Alas, I must admit that I’m not aware of an example that cannot be equally good expressed with the …ifNil: variant as with the [] once variant. So Long, -Tobias |
On 7/22/2010 11:07 PM, Tobias Pape wrote:
> > Am 2010-07-23 um 07:39 schrieb Andreas Raab: >> >> However, I do take Travis' point about compile time expressions. Here the whole point is that you really want the expression to be reevaluated when the defining code changes which is not easily done the above (it's possible but bothersome). > > I think its good to point out that it is possible to > differentiate between one-time-expressions > and compile-time expressions. I think an expression > executed once, however, not at compile time can make > a pretty good lazy initializer in the sense you mentioned. > Alas, I must admit that I’m not aware of an example that > cannot be equally good expressed with the …ifNil: variant > as with the [] once variant. I would have to look at VW to see where it's used but I'm not going to reject it off-hand because there have been a few situations in the past where I could have used this; mostly to preserve the identity of otherwise equal objects. Cheers, - Andreas |
In reply to this post by commits-2
On 2010/07/22 20:42, [hidden email] wrote:
> Eliot Miranda uploaded a new version of Kernel to project The Trunk: > http://source.squeak.org/trunk/Kernel-eem.474.mcz > > ==================== Summary ==================== > > Name: Kernel-eem.474 > Author: eem > Time: 22 July 2010, 1:42:05.4 pm > UUID: 292f2cc5-ba81-4d03-a64f-4e7cb878d68f > Ancestors: Kernel-eem.473 > > BlockClosure>>#once Travis Griggs' neat idiom for interning values of computations. Use e.g. as in > myResourceMethod > ^[time-consuming computation] once So if I understand correctly, this allows a block to memoize itself but also, unlike most memoization schemes, also allows us to UNmemoize the block? That's a pretty neat trick! frank |
In reply to this post by Tobias Pape
2010/7/23 Tobias Pape <[hidden email]>:
> > Am 2010-07-23 um 07:39 schrieb Andreas Raab: >> >> However, I do take Travis' point about compile time expressions. Here the whole point is that you really want the expression to be reevaluated when the defining code changes which is not easily done the above (it's possible but bothersome). > > I think its good to point out that it is possible to > differentiate between one-time-expressions > and compile-time expressions. I think an expression > executed once, however, not at compile time can make > a pretty good lazy initializer in the sense you mentioned. > Alas, I must admit that I’m not aware of an example that > cannot be equally good expressed with the …ifNil: variant > as with the [] once variant. > We must distinguish compile time evaluation (Dolphin ##() ) and lazy evaluation (at first execution like [] once). The later is better because more robust to load order... The former suffer at code loading : some class/method used by initialization could be missing. [] once is equivalent to ifNil: [] indeed... But when I see some code crippled with ifNil: just to solve an initialization problem, I feel it's stinking personnally. It is unecessarily parasitically explicit. If I have a Shared variable used at ten places, then I have to pollute 10 places with ifNil: for caring of lazy initialization... We could have a lazy evaluation scheme like this: - every shared variable value is accessed by sending #value (at bytecode level) - every shared variable binding is initialized with an UninitializedBinding - UninitializedBinding>>value is an indirection ^value ifNil: [UninitializedException raise] ifNotNil: [self value: value value)] - UninitializedBinding>>value: anObject self become: (InitializedBinding key: key value: anObject) - some initializer methods have a pragma MyClass>>doSomeInitialization <initialize> ClassVar := someObject someMessage. - compiling above method triggers some initialization like (MyClass sharedVariables at: #ClassVar) setUninitialzedValue: (MessageSend receiver: MyClass selector: #doSomeInitialization). - UninitializedBinding>>setUninitialzedValue: anEvaluator value:= anEvaluator - InitializedBinding>>setUninitialzedValue: anEvaluator self become: (UninitializedBinding key: key value: anEvaluator) The positive effects are: - variables are initialized lazily as long as someone provides an <initialize> annotation. - you don't have to cripple code with nil - load order and initialization order are relaxed (think of the mess of class initialization at Monticelllo or any other package manager load) - every time you change the initializer, you reinitialize the shared variable I let you comment on the negative effects (complex architectures, time spent in sending value at runtime instead of fetching 2nd ivar etc...). Nicolas > > So Long, > -Tobias > |
On 23.07.2010 14:42, Nicolas Cellier wrote:
[stuff snipped] > [] once is equivalent to ifNil: [] indeed... But when I see some code > crippled with ifNil: just to solve an initialization problem, I feel > it's stinking personnally. It is unecessarily parasitically explicit. > If I have a Shared variable used at ten places, then I have to pollute > 10 places with ifNil: for caring of lazy initialization... [more stuff snipped] well the [] once block needs to be in a method. if the access to the global is also in a method (accessor), then the call-sites are indistinguishable. the shared var has one advantage imho: it can be easily reset and reinitialized. and one disadvantage: if you change the initialisation code, it does not enforce that the shared var is updated automatically. thats the main (and only) distinction i can see. just my 2c thanks wolfgang Nicolas > >> >> So Long, >> -Tobias >> > > |
In reply to this post by Tobias Pape
On Fri, 23 Jul 2010, Tobias Pape wrote:
> > Am 2010-07-23 um 07:39 schrieb Andreas Raab: >> >> However, I do take Travis' point about compile time expressions. Here the whole point is that you really want the expression to be reevaluated when the defining code changes which is not easily done the above (it's possible but bothersome). > > I think its good to point out that it is possible to > differentiate between one-time-expressions > and compile-time expressions. I think an expression > executed once, however, not at compile time can make > a pretty good lazy initializer in the sense you mentioned. > Alas, I must admit that I?m not aware of an example that > cannot be equally good expressed with the ?ifNil: variant > as with the [] once variant. Seaside 2.8 uses a literal array with a single element for this to cache files. It works something like this: fooFile | array | array := #(nil). ^(array at: 1) ifNil: [ array at: 1 put: 'alongbase64string' decodeBase64 ] These selectors are auto-generated. I think #once is (would be) a better solution. It could be hardly done with class (instance) variables. Levente > > > So Long, > -Tobias > |
2010/7/23 Levente Uzonyi <[hidden email]>:
> On Fri, 23 Jul 2010, Tobias Pape wrote: > >> >> Am 2010-07-23 um 07:39 schrieb Andreas Raab: >>> >>> However, I do take Travis' point about compile time expressions. Here the >>> whole point is that you really want the expression to be reevaluated when >>> the defining code changes which is not easily done the above (it's possible >>> but bothersome). >> >> I think its good to point out that it is possible to >> differentiate between one-time-expressions >> and compile-time expressions. I think an expression >> executed once, however, not at compile time can make >> a pretty good lazy initializer in the sense you mentioned. >> Alas, I must admit that I?m not aware of an example that >> cannot be equally good expressed with the ?ifNil: variant >> as with the [] once variant. > > Seaside 2.8 uses a literal array with a single element for this to cache > files. It works something like this: > > fooFile > > | array | > array := #(nil). > ^(array at: 1) ifNil: [ > array at: 1 put: 'alongbase64string' decodeBase64 ] > > These selectors are auto-generated. I think #once is (would be) a better > solution. It could be hardly done with class (instance) variables. > > > Levente > Hmm I did same trick long time ago... How do they have it working in VW with immutable literals ? Nicolas >> >> >> So Long, >> -Tobias >> > > |
On Fri, 23 Jul 2010, Nicolas Cellier wrote:
> 2010/7/23 Levente Uzonyi <[hidden email]>: >> On Fri, 23 Jul 2010, Tobias Pape wrote: >> >>> >>> Am 2010-07-23 um 07:39 schrieb Andreas Raab: >>>> >>>> However, I do take Travis' point about compile time expressions. Here the >>>> whole point is that you really want the expression to be reevaluated when >>>> the defining code changes which is not easily done the above (it's possible >>>> but bothersome). >>> >>> I think its good to point out that it is possible to >>> differentiate between one-time-expressions >>> and compile-time expressions. I think an expression >>> executed once, however, not at compile time can make >>> a pretty good lazy initializer in the sense you mentioned. >>> Alas, I must admit that I?m not aware of an example that >>> cannot be equally good expressed with the ?ifNil: variant >>> as with the [] once variant. >> >> Seaside 2.8 uses a literal array with a single element for this to cache >> files. It works something like this: >> >> fooFile >> >> | array | >> array := #(nil). >> ^(array at: 1) ifNil: [ >> array at: 1 put: 'alongbase64string' decodeBase64 ] >> >> These selectors are auto-generated. I think #once is (would be) a better >> solution. It could be hardly done with class (instance) variables. >> >> >> Levente >> > > Hmm I did same trick long time ago... > How do they have it working in VW with immutable literals ? array := #(nil) beMutable. Levente > > Nicolas > >>> >>> >>> So Long, >>> -Tobias >>> >> >> > > |
Free forum by Nabble | Edit this page |