Confusing issue with blocks

J J azreal1977 at hotmail.com
Wed Dec 13 06:51:03 UTC 2006


>From: "Boris Gaertner" <Boris.Gaertner at gmx.net>
>Reply-To: The general-purpose Squeak developers 
>list<squeak-dev at lists.squeakfoundation.org>
>To: "The general-purpose Squeak developers 
>list"<squeak-dev at lists.squeakfoundation.org>
>Subject: Re: Confusing issue with blocks
>Date: Tue, 12 Dec 2006 21:37:41 +0100
>
>The problem with our good old Squeak is that blocks cannot be used
>recursively. To see that problem. evaluate:
>

But this is what is confusing to me.  The way I'm using it is recursively 
and it is working.  Except in this one case.  Let me try to illistrate what 
I mean.  In the following [] means the nil list, which in my implimentation 
is LazyNil.

In the call that works, the code does the following for (years = 2006 ..., 
months = 1,8, days = 1,15,28):

2006 :: 1 :: 1
2006 :: 1 :: 15
2006 :: 1 :: 28
2006 :: 1 :: []            "List terminated, unwinds one level and starts 
over on next month"
2006 :: 8 :: 1
2006 :: 8 :: 15
2006 :: 8 :: 28
2006 :: 8 :: []
2006 :: []                  "Month list terminated, unwinds back to year"
2007 :: 1 :: 1
etc.

And this works.  But in the case that the month list is empty for 2007 I get 
the error:

2006 :: 1 :: 1             "as before"
2006 :: 1 :: 15
2006 :: 1 :: 28
2006 :: 1 :: []            "List terminated, unwinds one level and starts 
over on next month"
2006 :: 8 :: 1
2006 :: 8 :: 15
2006 :: 8 :: 28
2006 :: 8 :: []
2006 :: []                  "Month list terminated, unwinds back to year"
2007 :: []                  "No months for this year"
2008 :: 1 :: 1             "Should happen but does not"

This is what I don't get.  The two cases seem to me to be virtually 
identical.  It is as if squeak needs a list of at least one element to get 
constructed so some internal counter can get reset or something.  In Ocaml 
my implementation is almost word for word the same (obviously I use Ocaml 
idioms in ocaml but the behavior is exactly the same) and this part works.  
And in fact in the stack trace I see that Smalltalk does indeed calculate 
the next date (2008,1,1 in this example), it just throws the exception.  
Very strange.

>In your code, you should modify method
>LazyElement>> inject:into:
>
>inject: aValue into: aBinaryBlock
>  "NOTE: This method does not behave as a normal
>   inject into.  It actually behaves exactly like fold_right"
>
>  ^ aBinaryBlock clone   " <-  clone here "
>        value: head
>        value: (LazyValue delay: [ tail force inject: aValue into:
>aBinaryBlock])
>
>That should help.

Actually I had the clone in the place you suggest and inside the delay.  
Every place you see a block used in that code has had a clone and a copy 
statement at some point, but nothing has fixed it so far.  I took them out 
to make sure the meaning of the code was clear as well as make the bug 
easily reproducable.  If you run the method "showWorking" it produces 500 
dates without incident, despite being a completely recursive algorythm.  If 
you run "showNotWorking" you get the failure behavior.

>A slightly different approach introduces an instance method
>that creates a block instance:
>
>flattenBlock
>
>    ^[:p :e| p append: e ]
>
>and modifies methods flatten and inject:into:
>
>flatten
>
>  ^ self inject: LazyNil new into: [self flattenBlock]
>
>
>inject: aValue into: aBinaryBlockGenerator
>  "NOTE: This method does not behave as a normal inject into.  It actually
>behaves exactly like fold_right"
>
>  ^ aBinaryBlockGenerator value "  this value calls a block that creates
>                              a new block instance "
>      value: head
>       value: (LazyValue delay: [ tail force inject: aValue into:
>aBinaryBlockGenerator])
>
> > Thanks,
> > Jason
>
>I know that this is tricky stuff, but I also hope that my remarks
>are to some extent helpful.
>

Of course.  Thank you very much for your help and response.  But as I have 
described this, I believe I figured out what the issue is:

In my implementation a LazyList is either a LazyNil (meaning empty list) or 
a two element object that the first object is a real value and the second is 
basically a function that returns the next sequence recursively (i.e. the 
next sequence can again be a LazyNil or a function).

The consequence of this is that you can have as much complicated nested 
lists, method calls etc. as you want but only 1 value will actually be 
calculated.  But each time a value is asked for *1* must be returned.  So in 
the case where each list has a value, the algorithm must only call each 
function once to get that value, all the recursive calls are delayed.  But 
in the case that months returns nothing, the algorithm can't go into a 
delay, it has to continue searching until it comes up with something.  This 
is creating the case where the same block actually ends up being used twice 
in the same call stack.

So that leads to the question, why didn't cloning everything fix it? :(  
What would I need to do to get squeak to instantiate closures instead of 
blockContexts?

I wish that one of the following 3 things would happen:
- Squeak would switch completely to proper closures
- Squeak would, during compile phase, determine if the block should be a 
closure or block context (e.g. does the block contain ^?)
- Squeak would provide some simple way to say "please instantiate these 
specific blocks as closures" (maybe this one exists?)

Thanks,
Jason

_________________________________________________________________
View Athlete’s Collections with Live Search 
http://sportmaps.live.com/index.html?source=hmemailtaglinenov06&FORM=MGAC01




More information about the Squeak-dev mailing list