The Trunk: Kernel-eem.474.mcz

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
15 messages Options
Reply | Threaded
Open this post in threaded view
|

The Trunk: Kernel-eem.474.mcz

commits-2
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!


Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Kernel-eem.474.mcz

Andreas.Raab
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

Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Kernel-eem.474.mcz

Eliot Miranda-2


On Thu, Jul 22, 2010 at 5:14 PM, Andreas Raab <[hidden email]> wrote:
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.

...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

Cheers,
 - Andreas




Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Kernel-eem.474.mcz

Andreas.Raab
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

Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Kernel-eem.474.mcz

Travis Griggs-4

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."


Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Kernel-eem.474.mcz

Eliot Miranda-2
In reply to this post by Andreas.Raab


On Thu, Jul 22, 2010 at 7:57 PM, Andreas Raab <[hidden email]> 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.

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.


Cheers,
 - Andreas

best
Eliot 


Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Kernel-eem.474.mcz

Andreas.Raab
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

Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Kernel-eem.474.mcz

Tobias Pape

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
Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Kernel-eem.474.mcz

Andreas.Raab
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

Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Kernel-eem.474.mcz

Frank Shearar
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

Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Kernel-eem.474.mcz

Nicolas Cellier
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
>

Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Kernel-eem.474.mcz

Wolfgang Eder
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
>>
>
>


Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Kernel-eem.474.mcz

Levente Uzonyi-2
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
>

Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Kernel-eem.474.mcz

Nicolas Cellier
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
>>
>
>

Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Kernel-eem.474.mcz

Levente Uzonyi-2
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 ?
IIRC they send #beMutable to the array. So the first line is like:
array := #(nil) beMutable.


Levente

>
> Nicolas
>
>>>
>>>
>>> So Long,
>>>        -Tobias
>>>
>>
>>
>
>