[squeak-dev] Block closure and scope problem

Levente Uzonyi leves at elte.hu
Fri May 27 07:10:03 UTC 2011


On Fri, 27 May 2011, Rodney Polkinghorne wrote:

> Hi all
>
> I'm trying to build a morph whose co-ordinates are set by blocks.  My
> current attempt is below.
>
> When I did "(LiteralDot locus: 100 at 100) openInWorld" in a workspace,
> the call of locusGetter in computeBounds raised a "Block can not
> return" error.  The debugger and inspector present a trail of
> BlockContexts and MessageContexts.  This failed to enlighten me.

See my comments below.

>
> I haven't a clue what self and super refer to in a closure, but I

self means the defining method's receiver, super means the same object, 
but the methods are looked up in one of it's superclasses. Going upwards 
the superclass chain, the first implementor's method will be used.

> thought I knew how lexical variables worked.  Apparently not.  Would
> anyone care to explain, or point me to an explanation?
>
> Rodney
>
>
> Change set follows:
>
> 'From Squeak4.2 of 4 February 2011 [latest update: #10966] on 27 May
> 2011 at 4:40:43 pm'!
> Smalltalk renameClassNamed: #StickMorph as: #DotMorph!
> Morph subclass: #DotMorph
>    instanceVariableNames: 'locusGetter locusSetter'
>    classVariableNames: ''
>    poolDictionaries: ''
>    category: 'UserObjects'!
>
> !DotMorph commentStamp: 'resp 5/27/2011 16:27' prior: 0!
> I am a dot 4 pixels in diameter.  My center is computed by a block,
> which is stored in my instance variable locusGetter.  Subclasses
> should initialize this before I am drawn.!
>
> Smalltalk renameClassNamed: #ToyStick as: #LiteralDot!
> DotMorph subclass: #LiteralDot
>    instanceVariableNames: 'locus'
>    classVariableNames: ''
>    poolDictionaries: ''
>    category: 'UserObjects'!
>
> !LiteralDot commentStamp: 'resp 5/27/2011 16:35' prior: 0!
> I am a dot, whose position is stored explicitly in my instance
> variable locus.  My constructor sends me an initializeBlocks message.
> This sets my locusGetter to a block that is supposed to return the
> value of that variable.  It actually raises a "cannot return" error.!
>
>
> !DotMorph methodsFor: 'as yet unclassified' stamp: 'resp 5/27/2011 16:28'!
> drawOn: aCanvas
>
>    self computeBounds.
>    aCanvas fillOval: self bounds color: Color black.! !
>
> !DotMorph methodsFor: 'private' stamp: 'resp 5/27/2011 16:28'!
> computeBounds
>
>    bounds := Rectangle center: locusGetter value extent: 4! !
>
> !DotMorph methodsFor: 'private' stamp: 'resp 5/27/2011 16:28'!
> locusGetter: aBlock
>
>    locusGetter := aBlock.
> ! !
>
>
> !LiteralDot methodsFor: 'accessing' stamp: 'resp 5/27/2011 16:09'!
> locus: aPoint
>    locus := aPoint! !
>
> !LiteralDot methodsFor: 'as yet unclassified' stamp: 'resp 5/27/2011 16:10'!
> initializeBlocks
>    "This must be an instance method for the blocks to close over the
> right environment."
>
>    self locusGetter: [ ^ locus ]; locusSetter: [ :p | locus := p ]! !

Here ^ means return from the defining method. Since the locusGetter block 
won't be evaluated from the #initializeBlocks method, it won't be able to 
return from it, that's why you get the "Block cannot return" error. Since 
blocks return the result of the last expression in them, you should simply 
write [ locus ] instead.

>
>
> !LiteralDot class methodsFor: 'as yet unclassified' stamp: 'resp
> 5/27/2011 16:08'!
> locus: aPoint
>    ^ (super new)

It's pointless to use super instead of self if you don't implement #new in 
LiteralDot, because in both cases one of the superclass' #new method will 
be used.


Levente

>        initializeBlocks;
>        locus: aPoint
>
>        ! !
>
>
> !LiteralDot reorganize!
> ('accessing' locus:)
> ('as yet unclassified' initializeBlocks)
> !
>
> DotMorph removeSelector: #locusSetter:!
>
> !DotMorph reorganize!
> ('as yet unclassified' drawOn:)
> ('private' computeBounds locusGetter:)
> !
>
>



More information about the Squeak-dev mailing list