Delaying execution of a piece of code

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

Delaying execution of a piece of code

Yar Hwee Boon
I would like to be able to define a piece of code and later play it
back. This probably sounds very similar to block closure, but I would
like the values used in the block to take the value at the point of
creation of the block. Something like the code below, but would like
the value printed to be 1 instead of 2. Did I miss a simple way of
doing this? Thanks.

===
index := 1.
block := [ Transcript show: index displayString ].
index := index + 1.
block value.
===

HweeBoon


Reply | Threaded
Open this post in threaded view
|

Re: Delaying execution of a piece of code

Chris Uppal-3
Yar Hwee Boon wrote:

> index := 1.
> block := [ Transcript show: index displayString ].
> index := index + 1.
> block value.

That's a deficiency in the current implementation of Blocks which, I believe,
it is going to be fixed in Dolphin 6.

In the meantime, the easiest way to get the effect you are after is probably to
factor out the creation of the block into a separate method.  Something like:

======
index := 1.
block := self makeBlock: index.
index := index + 1.
block value.
======

where the definition of #makeBlock is:

======
makeBlock: anIndex
    ^ [Transcript display: anIndex].
======

Obviously, if you want to do this kind of thing from Workspaces then you'll
have to define some kind of helper class to hold #makeBlock:

    -- chris


Reply | Threaded
Open this post in threaded view
|

Re: Delaying execution of a piece of code

Chris Uppal-3
I wrote:

> That's a deficiency in the current implementation of Blocks which, I
> believe, it is going to be fixed in Dolphin 6.

Actually, now I think of it, this code *isn't* exhibiting the deficiency I was
thinking of.  Sorry.

The workaround I posted still works, though.

    -- chris


Reply | Threaded
Open this post in threaded view
|

Re: Delaying execution of a piece of code

Blair McGlashan
"Chris Uppal" <[hidden email]> wrote in message
news:40640cf5$0$65060$[hidden email]...
> I wrote:
>
> > That's a deficiency in the current implementation of Blocks which, I
> > believe, it is going to be fixed in Dolphin 6.
>
> Actually, now I think of it, this code *isn't* exhibiting the deficiency I
was
> thinking of.  Sorry.
>

Yes, that's right. For the benefit of others what Chris is referring to (I
assume) is that if one writes:

> index := 1.
> block := [ Transcript show: index displayString ].
> index := index + 1.
> block value.

Then regardless of whether one has Smalltalk-80 style blocks or proper
closures, the value of index will be shared. This is because it is assigned
to after the point where it is "captured" by the block. If however one had
written:

blocks := (1 to: 5) collect: [:each | [ Transcript display: each]].
blocks do: [:each | each value] separatedBy: [Transcript nextPutAll: ', '].
Transcript cr.

Then that would result yield the following results:

D5:     5, 5, 5, 5, 5
D6:     1, 2, 3, 4, 5

> The workaround I posted still works, though.

The workaround of extracting the block creation to a separate method (and
therefore a separate activation) will work in almost all circumstances that
full closures are needed. It won't help with recursive blocks though.

Regards

Blair


Reply | Threaded
Open this post in threaded view
|

Re: Delaying execution of a piece of code

Yar Hwee Boon
In reply to this post by Chris Uppal-3
"Chris Uppal" <[hidden email]> wrote in message news:<[hidden email]>...

> Yar Hwee Boon wrote:
>
> > index := 1.
> > block := [ Transcript show: index displayString ].
> > index := index + 1.
> > block value.
>
> That's a deficiency in the current implementation of Blocks which, I believe,
> it is going to be fixed in Dolphin 6.
>
> In the meantime, the easiest way to get the effect you are after is probably to
> factor out the creation of the block into a separate method.  Something like:
>
> ======
> index := 1.
> block := self makeBlock: index.
> index := index + 1.
> block value.
> ======
>
> where the definition of #makeBlock is:
>
> ======
> makeBlock: anIndex
>     ^ [Transcript display: anIndex].
> ======
>
> Obviously, if you want to do this kind of thing from Workspaces then you'll
> have to define some kind of helper class to hold #makeBlock:
>
>     -- chris


Thanks. But actually how does a block closure actually handle the
variable(s) it is passed? I can't understand why your suggestion works
(although it works of course). And actually I am not trying to do this
in a workspace, rather, I'm trying to defer painting of text/lines
onto a PrinterCanvas into the appropriate page, eg. when a table that
I am printing spans across multiple pages, I check that it is out of
bounds of the current page and I run the code below to adjust the
coordinates and then store the block to be #value-ed late when I am at
the correct page.


========
report atPage: 2 addPendingPrint: [
                                        | adjustedOrigin |
                                        adjustedOrigin := position - 0@400.
                                        canvas
                                                formatText: each
                                                in: (Rectangle origin: adjustedOrigin extent: width@height)
                                                flags: ((columns at: index) justification)].].
========

HweeBoon


Reply | Threaded
Open this post in threaded view
|

Re: Delaying execution of a piece of code

Bill Schwab-2
> Thanks. But actually how does a block closure actually handle the
> variable(s) it is passed? I can't understand why your suggestion works
> (although it works of course). And actually I am not trying to do this
> in a workspace, rather, I'm trying to defer painting of text/lines
> onto a PrinterCanvas into the appropriate page, eg. when a table that
> I am printing spans across multiple pages, I check that it is out of
> bounds of the current page and I run the code below to adjust the
> coordinates and then store the block to be #value-ed late when I am at
> the correct page.

Would a block with one or more arguments (the page number, etc.) work?

Creating the block might look like this:

aBlock := [ :pageNumber | | adjustedOrigin |
   adjustedOrigin := position - ( (0@400) * ( 1@pageNumber ) ).
   canvas
      formatText: each
      in: (Rectangle origin: adjustedOrigin extent: width@height)
      flags: ((columns at: index) justification)].
].

and you would evaluate with

  aBlock value:2.

to print page number 2 (or maybe three depending on zero or unit offset).
Of course, you could always alter the block to get the page offset you want.
You can also have more parameters:

   aBlock := [ :x :y :z |   ].
   aBlock value:1 value:3.0 value:100.

Note that, because of the zero, (0@400) * ( 1@pageNumber ) is equivalent to
(0@400)*pageNumber, but it is worth remembering that points add and multiply
in useful ways, as do rectangles.

Have a good one,

Bill

--
Wilhelm K. Schwab, Ph.D.
[hidden email]


Reply | Threaded
Open this post in threaded view
|

Re: Delaying execution of a piece of code

Chris Uppal-3
In reply to this post by Yar Hwee Boon
Yar Hwee Boon wrote:

> Thanks. But actually how does a block closure actually handle the
> variable(s) it is passed? I can't understand why your suggestion works
> (although it works of course).

Well, I don't know the details of how it's implemented, if that's what you are
asking.

But the essential points are that:
   - each time a method is called, it is given a new set of variables,
   - a block *shares* the variables of its containing method call,
   - those variables continue to exist for as long as the block does (even
     if the method has returned).

So when you evaluate:

    block := self makeBlock: 22.

that will call the method:

    makeBlock: anIndex
        ^ [Transcript display: anIndex].

Now, that invocation of the method has it's own variable 'anIndex' which is set
to 22.  The method creates a new block object that shares 'anIndex', and then
when the method returns the block *still* has that variable.  So if you send
#value to block, it will print the value of that variable (22) on the
transcript.

Now, if you call the method a second time:

    anotherBlock := self makeBlock: 77.

then this call to #makeBlock will have its own version of the variable
'anIndex' (in this case set to 77), and so the new block that it answers will
print 77 on the transcript each time you send #value to it.

It *may* help to imagine a very simple implementation -- this stuff isn't
*really* implemented like this, but it is supposed to act as if it were.   When
you invoke a method, the system allocates a new object to hold the parameters
and local variables of that method.  Call the object a "context" (it could also
be called a "stack frame" or "activation record").  Each method *execution* has
its own context, and references to local variables and parameters are
implemented by looking up slots in the context object.  Normally contexts are
created when a method is called, and are discarded and garbage-collected when
the method returns.  However all the blocks created as a method executes also
have references to the context (so that they can share access to the
variables), and so if a block survives past the end of the method (as in my
example where I return the new block from the method) then the context object
will also survive for as long as the block needs it.  Then if I call the method
a second time, a second context will be created and it too will survive for as
long as the second block needs it.


> [...] I'm trying to defer painting of text/lines
> onto a PrinterCanvas into the appropriate page, eg. when a table that
> I am printing spans across multiple pages, I check that it is out of
> bounds of the current page and I run the code below to adjust the
> coordinates and then store the block to be #value-ed late when I am at
> the correct page.

That's nice, elegant, idea.  It's a shame that it doesn't quite work with
standard Smalltalk semantics :-(

> ========
> report atPage: 2 addPendingPrint: [
> > adjustedOrigin |
> adjustedOrigin := position - 0@400.
> canvas
> formatText: each
> in: (Rectangle origin: adjustedOrigin extent: width@height)
> flags: ((columns at: index) justification)].].
> ========

The problem is that the block you create inline shares the "position",
"canvas", etc, variables with the method call where is was created.  So all the
blocks created by that *single* method call will share the same variables.  All
you can do (without redesigning your code completely) is to ensure you use a
fresh method call to create each block so that all the blocks have their "own"
independent variables.


*** Digression warning! ****

This way of using blocks is so nice that it really is a shame that it doesn't
work without messing around with helper methods.  If method calls were really
implemented in the simple way I sketched above, then it would be quite simple
to "freeze" a block by having it take a copy of its context.  That would ensure
that it had it's own unshared copy of the variables and so it would be
unaffected by any subsequent changes to their values.

As an *experiment*, I tried adding this method to BlockClosure

============
    freeze
        self outer: (self outer copy).
============

which attempts to "freeze" a block by making a copy of its context.  I was
surprised to find that it *does* appear to work!  I don't know enough (not
*nearly* enough) about Dolphin meta-programming to judge if it's really safe
(it breaks the debugger, for a start), so I certainly wouldn't dare use it in
real code.

Blair, if you are reading, could you comment on the safety of the idea ?
Please feel free to comment on the sanity too ;-)

*** Digression end ****

    -- chris


Reply | Threaded
Open this post in threaded view
|

Re: Delaying execution of a piece of code

Blair McGlashan
"Chris Uppal" <[hidden email]> wrote in message
news:[hidden email]...

> Yar Hwee Boon wrote:
> > [...] I'm trying to defer painting of text/lines
> > onto a PrinterCanvas into the appropriate page, eg. when a table that
> > I am printing spans across multiple pages, I check that it is out of
> > bounds of the current page and I run the code below to adjust the
> > coordinates and then store the block to be #value-ed late when I am at
> > the correct page.
>
> That's nice, elegant, idea.  It's a shame that it doesn't quite work with
> standard Smalltalk semantics :-(
>

Do you mean that it doesn't work with standard Smalltalk-80 semantics, or
that it doesn't work with full closure semantics?

> > ========
> > report atPage: 2 addPendingPrint: [
> > > adjustedOrigin |
> > adjustedOrigin := position - 0@400.
> > canvas
> > formatText: each
> > in: (Rectangle origin: adjustedOrigin extent: width@height)
> > flags: ((columns at: index) justification)].].
> > ========
>
> The problem is that the block you create inline shares the "position",
> "canvas", etc, variables with the method call where is was created.  So
all the
> blocks created by that *single* method call will share the same variables.
All
> you can do (without redesigning your code completely) is to ensure you use
a
> fresh method call to create each block so that all the blocks have their
"own"
> independent variables.
>
>
> *** Digression warning! ****
>
> This way of using blocks is so nice that it really is a shame that it
doesn't
> work without messing around with helper methods.  If method calls were
really
> implemented in the simple way I sketched above, then it would be quite
simple
> to "freeze" a block by having it take a copy of its context.  That would
ensure
> that it had it's own unshared copy of the variables and so it would be
> unaffected by any subsequent changes to their values.
>

Well it would work with full closure semantics as in D6, as long as the
captured variables are not written after they are captured by the block, and
are not assigned to in the block. Where the variables are written-after-read
in this way, then it is assumed that sharing is the desired behaviour, i.e.

a := 1.
block := [a].
a := 2.
block value    => 2

This would be the case in D5 or D6, the former sharing 'a' or any other temp
closed over by the block because it is limited to working that way, the
latter deliberately choosing to share the variable.

Where the 'sharing' behaviour is not wanted in cases like this, then in D5
one has to have a separate method activation because, as you say, all
variables are allocated per method activation. In D5 method activations will
have a stackframe, and may have a heap allocated context object as well if
they create any blocks. When a D5 method activation requires a context, all
the temps are stored in the context regardless of whether they should really
be shared (or shareable). Block activations in D5 do have a stackframe, but
there are no temporary variables in the frame. Even the arguments get
"popped" off the stack back into the home method context.

In D6 all method activations still have a stack frame (the format of which
is quite similar to D5s in fact). The may also have a "Context" object if
needed to hold shared variables, but this is really little more than an
array of slots. It doesn't hold any per-activation information, so its not
like a Smalltalk-80 context. In D6 block activations are essentially the
same as method activations, and so any variables local to the block, such as
arguments or its own temporaries are allocated in the blocks own stack
frame. Blocks might also have their own Context objects if they themselves
enclose blocks which share their variables. In the general case it is
possible to share variables from a chain of outer contexts, although this
rarely occurs in practice.

Rather than add further detail here, you can read the class comment of
Dolphin 6's BlockClosure class at:

http://object-arts.com/Lib/Update/Dolphin/5.1/BlockClosure.txt

In D6 if one wants to "snapshot" the value of a shared variable that is
changed after block creation, one can do something like this:

a := 1.
block := [:a1 | [a1]] value: a.
a := 2.
block value    => 1

This makes it plain that one is deliberately freezing the value in the
block. The basic rule is that any variable you don't want to share but do
want to change after the block has been created needs to be frozen by
passing it as an argument into another activation. In D5 this has to be done
as a separate method, since only methods have proper activations, but in D6
it can be done inline as above. Frankly though it is not something I have
ever needed to do as the standard rules for closing over variables are
usually appropriate.

> As an *experiment*, I tried adding this method to BlockClosure
>
> ============
>     freeze
>         self outer: (self outer copy).
> ============
>
> which attempts to "freeze" a block by making a copy of its context.  I was
> surprised to find that it *does* appear to work!  I don't know enough (not
> *nearly* enough) about Dolphin meta-programming to judge if it's really
safe
> (it breaks the debugger, for a start), so I certainly wouldn't dare use it
in
> real code.
>

This is similar to the idea behind the Squeak (and I presume Smalltalk-80)
#fixTemps message. It should be safe as long as you don't create a situation
where the "frozen" block might attempt a ^-return after the original method
has returned, which would almost certainly crash the system. Actually I
would suggest modifying your implementation by adding a method to
MethodContext that sets its frame instance variable to zero, then call this
from BlockClosure>>freeze on the outer copy.

As with #fixTemps, you now have two completely separate contexts so any
variables you may have intended to share will no longer be sharing the same
slot (though of course it is still possible to have "variables" shared
between the contexts by by adding a further level of indirection).

> Blair, if you are reading, could you comment on the safety of the idea ?
> Please feel free to comment on the sanity too ;-)
>

Personally I'd rather add another method. There is no fixTemps in Dolphin
because it is really a hack with hidden effects that are not immediately
obvious to anyone with a basic understanding of Smalltalk semantics. For
example you'll end up capturing more state than you need (a weakness of
Smalltalk-80 blocks anyway of course). It really won't be needed with D6
anyway. If you want to experiment with it in the meantime, then it would be
interesting to hear of your experiences.

Regards

Blair


Reply | Threaded
Open this post in threaded view
|

Re: Delaying execution of a piece of code

Yar Hwee Boon
"Blair McGlashan" <[hidden email]> wrote in message news:<c49hf4$2fr70r$[hidden email]>...

snipped

>
> In D6 if one wants to "snapshot" the value of a shared variable that is
> changed after block creation, one can do something like this:
>
> a := 1.
> block := [:a1 | [a1]] value: a.
> a := 2.
> block value    => 1
>
> This makes it plain that one is deliberately freezing the value in the
> block. The basic rule is that any variable you don't want to share but do
> want to change after the block has been created needs to be frozen by
> passing it as an argument into another activation. In D5 this has to be done
> as a separate method, since only methods have proper activations, but in D6
> it can be done inline as above. Frankly though it is not something I have
> ever needed to do as the standard rules for closing over variables are
> usually appropriate.

As for my original need (to defer painting of lines, text on the
current page to another page on a PrinterCanvas), I have tried using
the Command pattern, creating a subclass for each drawing method, such
as #formatText:in:flags: and #text:at: and passing in the relevant
parameters upon instantiation instead of using blocks. This will
probably lead to a handful or 2 of subclasses, but seems to fit my
needs so far.. Does anyone has any comment regarding this approach
compared to using blocks as described by other Chris, etc earlier?

snipped

Hwee Boon


Reply | Threaded
Open this post in threaded view
|

Re: Delaying execution of a piece of code

Chris Uppal-3
Yar Hwee Boon wrote:

> As for my original need (to defer painting of lines, text on the
> current page to another page on a PrinterCanvas), I have tried using
> the Command pattern, creating a subclass for each drawing method, such
> as #formatText:in:flags: and #text:at: and passing in the relevant
> parameters upon instantiation instead of using blocks. This will
> probably lead to a handful or 2 of subclasses, but seems to fit my
> needs so far.. Does anyone has any comment regarding this approach
> compared to using blocks as described by other Chris, etc earlier?

It's a bit late to reply, but...

FWIW, that seems like a sensible approach to me.

The blocks approach is more elegant (if it worked ;-) but there's a lot to be
said for keeping things simple.

    -- chris


Reply | Threaded
Open this post in threaded view
|

Re: Delaying execution of a piece of code

Chris Uppal-3
In reply to this post by Blair McGlashan
Blair McGlashan wrote:

> > > [...] I'm trying to defer painting of text/lines
> > > onto a PrinterCanvas into the appropriate page, eg. when a table that
> > > I am printing spans across multiple pages, I check that it is out of
> > > bounds of the current page and I run the code below to adjust the
> > > coordinates and then store the block to be #value-ed late when I am at
> > > the correct page.
> >
> > That's nice, elegant, idea.  It's a shame that it doesn't quite work
> > with standard Smalltalk semantics :-(
> >
>
> Do you mean that it doesn't work with standard Smalltalk-80 semantics, or
> that it doesn't work with full closure semantics?

The approach doesn't work with either semantics.  It's looking for a
(functional programming-style) closure over the current values of variables,
rather than a closure over the variables (slots) themselves.


> Rather than add further detail here, you can read the class comment of
> Dolphin 6's BlockClosure class at:
>
> http://object-arts.com/Lib/Update/Dolphin/5.1/BlockClosure.txt


Very interesting.  Thanks.


> In D6 if one wants to "snapshot" the value of a shared variable that is
> changed after block creation, one can do something like this:
>
> a := 1.
> block := [:a1 | [a1]] value: a.
> a := 2.
> block value    => 1

Not wanting to be rude ;-) but that's *really* ugly....

I think the idea of freezing a block is a useful idiom to have available.
Sure, there are other ways of getting the same effect -- breaking the variables
out as parameters to a helper method in simple cases, or factoring the local
variables into the instvars of a new object for more complicated cases -- but I
think that there are classes of problems where either of those techniques would
complicate, rather than clarify, the code.

Compare the case of continuations.  They aren't something you're going to need
every day, but they are handy to have and can simplify the expression of some
algorithms enormously (not that I've actually used continuations yet, but...).


> > ============
> >     freeze
> >         self outer: (self outer copy).
> > ============
> >
> > which attempts to "freeze" a block by making a copy of its context.  I
> > was surprised to find that it *does* appear to work!  I don't know
> > enough (not *nearly* enough) about Dolphin meta-programming to judge if
> > it's really safe (it breaks the debugger, for a start), so I certainly
> > wouldn't dare use it in real code.
> >
>
> This is similar to the idea behind the Squeak (and I presume Smalltalk-80)
> #fixTemps message.

Ah!  So that's what #fixTemps is for.  I'd noticed it in the Squeak image, but
(probably because "temps" doesn't mean "local variables" to me) hadn't clicked
to what it was doing.  A quick scan through the Squeak image suggests that it
is rather over-used...


> It should be safe as long as you don't create a
> situation where the "frozen" block might attempt a ^-return after the
> original method has returned, which would almost certainly crash the
> system. Actually I would suggest modifying your implementation by adding
> a method to MethodContext that sets its frame instance variable to zero,
> then call this from BlockClosure>>freeze on the outer copy.

Thanks for the guidance.  I shall return to this later (currently, I'm too busy
being intensely lazy ;-)


> There is no fixTemps in Dolphin
> because it is really a hack with hidden effects that are not immediately
> obvious to anyone with a basic understanding of Smalltalk semantics.

I wouldn't call it a hack.  I agree that it's not something I'd want to see
used much, but I think it's a useful, and semantically sound, albeit "advanced"
technique.


> It really won't be needed with D6
> anyway. If you want to experiment with it in the meantime, then it would
> be interesting to hear of your experiences.

I think it will be just as useful in D6.  (That's to say, I don't think it's an
appropriate "fix" for problems caused by Dolphin's current block behaviour --
I'd rather reserve it for places where it conveys real meaning.)

I am, BTW, not arguing that you should add it to D6 (though I'd not be sorry if
you did).  It seems easy enough to do myself.

    -- chris


Reply | Threaded
Open this post in threaded view
|

Re: Delaying execution of a piece of code

Blair McGlashan
"Chris Uppal" <[hidden email]> wrote in message
news:[hidden email]...

> Blair McGlashan wrote:
> .....
> > In D6 if one wants to "snapshot" the value of a shared variable that is
> > changed after block creation, one can do something like this:
> >
> > a := 1.
> > block := [:a1 | [a1]] value: a.
> > a := 2.
> > block value    => 1
>
> Not wanting to be rude ;-) but that's *really* ugly....

Perhaps, but one can understand that the block will definitely not share 'a'
without having to know about what magic is performed by #fixTemps, or
whatever better name one chooses for it - it certainly needs a name that
reveals that any shared temporary variables accessed in the block will no
longer be shared. Its also worth bearing in mind that the representation of
the context you end up with is still the full monty needed for sharing
variables, which is more general than you need.

Anyway here is a better way (don't know why it didn't occur to me before):

a := 1.
fixedA := a.
block := [fixedA].
a := 2.

This won't work if you assign to fixedA in the block, but why would you want
to do that anyway unless its value needed to be shared?

Frankly, though, for the most part one tends to find that the lexical
closure semantics do the right thing most of the time.

>...
> > This is similar to the idea behind the Squeak (and I presume
Smalltalk-80)
> > #fixTemps message.
>
> Ah!  So that's what #fixTemps is for.  I'd noticed it in the Squeak image,
but
> (probably because "temps" doesn't mean "local variables" to me) hadn't
clicked
> to what it was doing.  A quick scan through the Squeak image suggests that
it
> is rather over-used...

Put something like that in, and inevitably that will happen. Its a path of
least resistance thing, and exactly the reason we have avoided putting a
switch "statement"  into Dolphin.

>...[snip]...
> > There is no fixTemps in Dolphin
> > because it is really a hack with hidden effects that are not immediately
> > obvious to anyone with a basic understanding of Smalltalk semantics.
>
> I wouldn't call it a hack.  I agree that it's not something I'd want to
see
> used much, but I think it's a useful, and semantically sound, albeit
"advanced"
> technique.

To my mind it's a hack to workaround the Smalltalk-80 block limitations.
There might be an argument that the behaviour of being able to "unshare"
specific variables is useful, but I would prefer that be achieved at block
creation time, and syntactically. As I've mentioned, there is a way to do
that by using additional temporaries to which one assigns the value of the
temp that one wants to "fix". This is certainly more clunky than it could
be, but it is a matter of opinion as to whether the requirement is esoteric
enough that it shouldn't be any easier.

> > It really won't be needed with D6
> > anyway. If you want to experiment with it in the meantime, then it would
> > be interesting to hear of your experiences.
>
> I think it will be just as useful in D6.  (That's to say, I don't think
it's an
> appropriate "fix" for problems caused by Dolphin's current block
behaviour --
> I'd rather reserve it for places where it conveys real meaning.)

Well we're in agreement about the second bit anyway :-)

>
> I am, BTW, not arguing that you should add it to D6 (though I'd not be
sorry if
> you did).  It seems easy enough to do myself.

I think it appropriate that advanced users add this themselves.

Regards

Blair