[squeak-dev] Block closure and scope problem

Bert Freudenberg bert at freudenbergs.de
Fri May 27 10:51:16 UTC 2011


Levente commented on what you need to actually do to make your approach work.

I just want to comment that your approach is very unusual for Smalltalk. Smells lispy to me. Instead of storing specific closures in subclasses, you normally would just implement the getter and setter methods differently. Blocks are typically used to make a single class customizable, not for implementing subclass-specific behavior.

E.g. DotMorph would just use "self locus" in #computeBounds. LiteralDot would implement #locus as "^locus", and #locus: just by storing aPoint in its instance variable. No blocks needed.

- Bert -

On 27.05.2011, at 09:10, Levente Uzonyi wrote:

> 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