Question

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

Question

jdelgado
Hi all,

------------------------------------------------
My question is:
One thing I've observed is that local/temporal variables in the blocks
passed to #ifTrue:iFfalse are not stored in the separate remote (heap
allocated) array, but in the method context (see page 328 of Deep Into
Pharo to see what I mean). Is this true? why?

Thanks in advance,

Bests,

Jordi
------------------------------------------------

PS:
Now, to put that question in context, here comes a really long "rest of
the message" part.

We assume we have Continuation class >> #callcc: implemented as:
callcc: aBlock
        ^ Continuation currentDo: aBlock
(#callcc: can be found in ContinuationTests, but I prefer to change
its location. It does not matter at all where it is).

A version of whileTrue: can be implemented with callcc: and no
iterative constructs, let's call it BlockClosure >> #whileTrueCC:
whileTrueCC: aBlock
     | cont |
     cont := Continuation callcc: [ :cc | cc ].
     self value ifTrue: [ aBlock value.
                       cont value: cont]
                ifFalse: [^ nil].

and you can use it in the same way as #whileTrue:, for example:
| n |
n := 10.
[ n > 0 ] whileTrueCC: [
             Transcript show: n asString; cr.
             n := n-1 ]

I observed some strange behavior dealing with local variables and
continuations. Assume we have:

Version 1)
whileTrueCC: aBlock
     | cont tmp |
     tmp := 0.
     cont := Continuation callcc: [ :cc | cc ].
     self value ifTrue: [ aBlock value.
                          Transcript show: 'inside whileTrueCC: -> ', tmp
asString; cr.
                          tmp := tmp + 1.
                       cont value: cont]
                ifFalse: [^ nil].

Version 2)
whileTrueCC: aBlock
     | cont tmp |
     tmp := 0.
     [ tmp ].  "<-------- Only difference wrt Version 1 !!!! It does
nothing, apparently"
     cont := Continuation callcc: [ :cc | cc ].
     self value ifTrue: [ aBlock value.
                          Transcript show: 'inside whileTrueCC: -> ', tmp
asString; cr.
                          tmp := tmp + 1.
                       cont value: cont]
                ifFalse: [^ nil].

I try both versions with:
| n |
n := 4.
[ n > 0 ] whileTrueCC: [ n := n-1 ]

Version 1:
It writes in the Transcript:
inside whileTrueCC: -> 0
inside whileTrueCC: -> 0
inside whileTrueCC: -> 0
inside whileTrueCC: -> 0

Version 2:
It writes in the Transcript:
inside whileTrueCC: -> 0
inside whileTrueCC: -> 1
inside whileTrueCC: -> 2
inside whileTrueCC: -> 3

Which is ok, according to what I understood from Deep Into Pharo. The
thing is, why the ifTrue: block does not make tmp behave as when we close
it with '[ tmp ]' in version 2?

PS 2: The behavior is *exactly* the same in Version 2 if I close tmp
*after* grabbing the continuation, that is, if I go
     cont := Continuation callcc: [ :cc | cc ].
    [ tmp ].  "<-------- Only difference wrt Version 1 !!!! It does
nothing, apparently"
instead of
    [ tmp ].  "<-------- Only difference wrt Version 1 !!!! It does
nothing, apparently"
     cont := Continuation callcc: [ :cc | cc ].



Reply | Threaded
Open this post in threaded view
|

Re: Question

Eliot Miranda-2


On Wed, Oct 29, 2014 at 9:48 AM, <[hidden email]> wrote:
Hi all,

------------------------------------------------
My question is:
One thing I've observed is that local/temporal variables in the blocks
passed to #ifTrue:iFfalse are not stored in the separate remote (heap
allocated) array, but in the method context (see page 328 of Deep Into
Pharo to see what I mean). Is this true?

yes.
 
why?

because the blocks don't really exist.  they have been optimized away and the control flow implemented using conditional branches and jumps instead of  closures and polymorphism.  Hence there is no other scope to hold temps in other than the method context (or the block context of a block containing optimized blocks).

But realise that the remote temp vector is *only* used for variables that are referred to from more than one full scope (method or unoptimized block) that can change value after they have been closed-over.  If a variable is closed over but doesn't change then its value is copied into the block, and ends up in a temporary slot in the block's activation, not in the indirection vector.



Thanks in advance,

Bests,

Jordi
------------------------------------------------

PS:
Now, to put that question in context, here comes a really long "rest of
the message" part.

We assume we have Continuation class >> #callcc: implemented as:
callcc: aBlock
        ^ Continuation currentDo: aBlock
(#callcc: can be found in ContinuationTests, but I prefer to change
its location. It does not matter at all where it is).

A version of whileTrue: can be implemented with callcc: and no
iterative constructs, let's call it BlockClosure >> #whileTrueCC:
whileTrueCC: aBlock
     | cont |
     cont := Continuation callcc: [ :cc | cc ].
     self value ifTrue: [ aBlock value.
                          cont value: cont]
                ifFalse: [^ nil].

and you can use it in the same way as #whileTrue:, for example:
| n |
n := 10.
[ n > 0 ] whileTrueCC: [
             Transcript show: n asString; cr.
             n := n-1 ]

I observed some strange behavior dealing with local variables and
continuations. Assume we have:

Version 1)
whileTrueCC: aBlock
     | cont tmp |
     tmp := 0.
     cont := Continuation callcc: [ :cc | cc ].
     self value ifTrue: [ aBlock value.
                          Transcript show: 'inside whileTrueCC: -> ', tmp
asString; cr.
                          tmp := tmp + 1.
                          cont value: cont]
                ifFalse: [^ nil].

Version 2)
whileTrueCC: aBlock
     | cont tmp |
     tmp := 0.
     [ tmp ].  "<-------- Only difference wrt Version 1 !!!! It does
nothing, apparently"
     cont := Continuation callcc: [ :cc | cc ].
     self value ifTrue: [ aBlock value.
                          Transcript show: 'inside whileTrueCC: -> ', tmp
asString; cr.
                          tmp := tmp + 1.
                          cont value: cont]
                ifFalse: [^ nil].

I try both versions with:
| n |
n := 4.
[ n > 0 ] whileTrueCC: [ n := n-1 ]

Version 1:
It writes in the Transcript:
inside whileTrueCC: -> 0
inside whileTrueCC: -> 0
inside whileTrueCC: -> 0
inside whileTrueCC: -> 0

Version 2:
It writes in the Transcript:
inside whileTrueCC: -> 0
inside whileTrueCC: -> 1
inside whileTrueCC: -> 2
inside whileTrueCC: -> 3

Which is ok, according to what I understood from Deep Into Pharo. The
thing is, why the ifTrue: block does not make tmp behave as when we close
it with '[ tmp ]' in version 2?

See above.  The compiler is not smart enough to realise that the value of [tmp] is discarded, so it thinks there is a block created that closed over tmp which may be evaluated at any time (because it could get assigned to a variable and used later).  So tmp is modified *after* it has been closed over (in the while block) and hence the compiler puts it in a temp vector.


PS 2: The behavior is *exactly* the same in Version 2 if I close tmp
*after* grabbing the continuation, that is, if I go
     cont := Continuation callcc: [ :cc | cc ].
    [ tmp ].  "<-------- Only difference wrt Version 1 !!!! It does
nothing, apparently"
instead of
    [ tmp ].  "<-------- Only difference wrt Version 1 !!!! It does
nothing, apparently"
     cont := Continuation callcc: [ :cc | cc ].


--
best,
Eliot
Reply | Threaded
Open this post in threaded view
|

Re: Question

jdelgado
In reply to this post by jdelgado

Thanks a lot, Eliot!

Bests,

Jordi

> On Wed, Oct 29, 2014 at 9:48 AM, <[hidden email]> wrote:


>
>> Hi all,
>>
>> ------------------------------------------------
>> My question is:
>> One thing I've observed is that local/temporal variables in the blocks
>> passed to #ifTrue:iFfalse are not stored in the separate remote (heap
>> allocated) array, but in the method context (see page 328 of Deep Into
>> Pharo to see what I mean). Is this true?
>
>
> yes.
>
>
>> why?
>>
>
> because the blocks don't really exist. they have been optimized away and
> the control flow implemented using conditional branches and jumps instead
> of closures and polymorphism. Hence there is no other scope to hold
> temps
> in other than the method context (or the block context of a block
> containing optimized blocks).
>
> But realise that the remote temp vector is *only* used for variables that
> are referred to from more than one full scope (method or unoptimized
> block)
> that can change value after they have been closed-over. If a variable is
> closed over but doesn't change then its value is copied into the block,
> and
> ends up in a temporary slot in the block's activation, not in the
> indirection vector.
>
>
>
>> Thanks in advance,
>>
>> Bests,
>>
>> Jordi
>> ------------------------------------------------
>>
>> PS:
>> Now, to put that question in context, here comes a really long "rest of
>> the message" part.
>>
>> We assume we have Continuation class >> #callcc: implemented as:
>> callcc: aBlock
>> ^ Continuation currentDo: aBlock
>> (#callcc: can be found in ContinuationTests, but I prefer to change
>> its location. It does not matter at all where it is).
>>
>> A version of whileTrue: can be implemented with callcc: and no
>> iterative constructs, let's call it BlockClosure >> #whileTrueCC:
>> whileTrueCC: aBlock
>> | cont |
>> cont := Continuation callcc: [ :cc | cc ].
>> self value ifTrue: [ aBlock value.
>> cont value: cont]
>> ifFalse: [^ nil].
>>
>> and you can use it in the same way as #whileTrue:, for example:
>> | n |
>> n := 10.
>> [ n > 0 ] whileTrueCC: [
>> Transcript show: n asString; cr.
>> n := n-1 ]
>>
>> I observed some strange behavior dealing with local variables and
>> continuations. Assume we have:
>>
>> Version 1)
>> whileTrueCC: aBlock
>> | cont tmp |
>> tmp := 0.
>> cont := Continuation callcc: [ :cc | cc ].
>> self value ifTrue: [ aBlock value.
>> Transcript show: 'inside whileTrueCC: -> ',
>> tmp
>> asString; cr.
>> tmp := tmp + 1.
>> cont value: cont]
>> ifFalse: [^ nil].
>>
>> Version 2)
>> whileTrueCC: aBlock
>> | cont tmp |
>> tmp := 0.
>> [ tmp ]. "<-------- Only difference wrt Version 1 !!!! It does
>> nothing, apparently"
>> cont := Continuation callcc: [ :cc | cc ].
>> self value ifTrue: [ aBlock value.
>> Transcript show: 'inside whileTrueCC: -> ',
>> tmp
>> asString; cr.
>> tmp := tmp + 1.
>> cont value: cont]
>> ifFalse: [^ nil].
>>
>> I try both versions with:
>> | n |
>> n := 4.
>> [ n > 0 ] whileTrueCC: [ n := n-1 ]
>>
>> Version 1:
>> It writes in the Transcript:
>> inside whileTrueCC: -> 0
>> inside whileTrueCC: -> 0
>> inside whileTrueCC: -> 0
>> inside whileTrueCC: -> 0
>>
>> Version 2:
>> It writes in the Transcript:
>> inside whileTrueCC: -> 0
>> inside whileTrueCC: -> 1
>> inside whileTrueCC: -> 2
>> inside whileTrueCC: -> 3
>>
>> Which is ok, according to what I understood from Deep Into Pharo. The
>> thing is, why the ifTrue: block does not make tmp behave as when we
>> close
>> it with '[ tmp ]' in version 2?
>>
>
> See above. The compiler is not smart enough to realise that the value of
> [tmp] is discarded, so it thinks there is a block created that closed over
> tmp which may be evaluated at any time (because it could get assigned to a
> variable and used later). So tmp is modified *after* it has been closed
> over (in the while block) and hence the compiler puts it in a temp vector.
>
>
> PS 2: The behavior is *exactly* the same in Version 2 if I close tmp
>> *after* grabbing the continuation, that is, if I go
>> cont := Continuation callcc: [ :cc | cc ].
>> [ tmp ]. "<-------- Only difference wrt Version 1 !!!! It does
>> nothing, apparently"
>> instead of
>> [ tmp ]. "<-------- Only difference wrt Version 1 !!!! It does
>> nothing, apparently"
>> cont := Continuation callcc: [ :cc | cc ].
>>
>
>
> --
> best,
> Eliot
>