<!DOCTYPE html><html><head><title></title><style type="text/css">p.MsoNormal,p.MsoNoSpacing{margin:0}
p.MsoNormal,p.MsoNoSpacing{margin:0}</style></head><body><div>Thanks for the explanation! I needed a few days to grok it. Now I can reproduce the bug in a very simple class and also prevent it. I have a copy of Structure & Interpretation of Programming Languages and it is on my reading list, but I was a little bit lazy to read it. :)<br></div><div><br></div><div>Thanks again!<br></div><div>Regards,<br></div><div>Szabolcs<br></div><div><br></div><div>On Thu, Sep 28, 2023, at 23:57, <a href="mailto:ken.dickey@whidbey.com">ken.dickey@whidbey.com</a> wrote:<br></div><blockquote type="cite" id="qt" style=""><div>On 2023-09-28 13:06, Szabolcs Komáromi via Cuis-dev wrote:<br></div><div><br></div><div>> I need little bit more context to fully grasp the problem.<br></div><div><br></div><div>It is subtle. The crux is to know which closure captured values are <br></div><div>shared and which are unique.<br></div><div><br></div><div>The original code had an #action method that returned a closure which <br></div><div>captured a reference to the `action` instance variable. So each <br></div><div>invocation of the closure got a value from the `action` ivar.<br></div><div><br></div><div>In the #composedActionExample2 method, there is code to assign a new <br></div><div>composite closure which contains code to invoke both the new and the <br></div><div>original action-closures.<br></div><div><br></div><div>So when the composed action-closure is invoked, it does the new action <br></div><div>and then invokes the "original" action-closure which gets the value from <br></div><div>the action-ivar which _now_ contains the new, composite closure. This <br></div><div>closure invokes the new action and then invokes the second, "original" <br></div><div>closure which goes back to the ivar for its value, gets the composite <br></div><div>closure which... recurses a lot until it runs out of stack space.<br></div><div><br></div><div>By introducing a local variable into closure result of the #action <br></div><div>method, the composite closure then is invoked with the new action, then <br></div><div>the original action. The "original" action now returns the closed over <br></div><div>local (cached) closure and does NOT go to the ivar named #action, so the <br></div><div>expected result it achieved -- each action-closure is invoked only once. <br></div><div> Whew! :)<br></div><div><br></div><div>> Is there any recommended book/paper about BlockClosures in the Squeak <br></div><div>> family of Smalltalk dialects? Are the Blue Book or the Inside Smalltalk <br></div><div>> still relevant in this question?<br></div><div><br></div><div>My recollection is that early Smalltalks had Block Contexts, NOT <br></div><div>Closures, so I expect the Blue Book would be unhelpful in this case. I <br></div><div>would expect Scheme would be more helpful (e.g. Structure & <br></div><div>Interpretation of Programming Languages) if Wikipedia is too opaque.<br></div><div><br></div><div>The crux is when closure captured values are shared and when they are <br></div><div>unique.<br></div><div><br></div><div>A couple of interesting use-cases:<br></div><div><br></div><div>In Morphic-Games-Solitare, $CardTableMorph>>slide:to:nSteps:delay:next: <br></div><div>moves a card from one location to another. It uses closures to capture <br></div><div>position information.<br></div><div><br></div><div>vvv===vvv===vvv===vvv<br></div><div>slide: aMorph<br></div><div>to: endPoint<br></div><div>nSteps: numSteps<br></div><div>delay: milliSecondsDelay<br></div><div>next: nextAction<br></div><div><br></div><div> "Slide from current to new position -- in owner's coordinates"<br></div><div> "Nota Bene: Asynchronous. When complete, nextAction value"<br></div><div><br></div><div> | startPoint delta stepCount |<br></div><div> startPoint aMorph morphPosition.<br></div><div> delta (endPoint - startPoint) / numSteps.<br></div><div> stepCount := 0.<br></div><div> aMorph when: #morphicStep<br></div><div> evaluate: [ :ignoredArgument |<br></div><div>stepCount := stepCount + 1.<br></div><div>(stepCount < numSteps)<br></div><div>ifTrue: [<br></div><div>aMorph morphPosition:<br></div><div> (startPoint + (stepCount * delta)) rounded;<br></div><div>redrawNeeded<br></div><div>]<br></div><div>ifFalse: [ "done"<br></div><div>aMorph stopStepping.<br></div><div>aMorph morphPosition: endPoint.<br></div><div>aMorph removeActionsForEvent: #morphicStep.<br></div><div>nextAction value<br></div><div>]<br></div><div>].<br></div><div> aMorph startSteppingStepTime: milliSecondsDelay<br></div><div>^^^===^^^===^^^===^^^<br></div><div><br></div><div>CardTableMorph>>addingCardOrCards:toContainer: uses closures to <br></div><div>capture/remember undo actions.<br></div><div><br></div><div>vvv===vvv===vvv===vvv<br></div><div>addingCardOrCards: cardMorph toContainer: cardContainer<br></div><div><br></div><div> "Container is about to add a Card or Cards. Remember undo action."<br></div><div><br></div><div> | startContainer |<br></div><div> startContainer := cardMorph valueOfProperty: #moveStart.<br></div><div> cardMorph removeProperty: #moveStart.<br></div><div> ((self inUndo) or: [startContainer = cardContainer ])<br></div><div> ifFalse: [<br></div><div>self pushUndoObject:<br></div><div>[ :table :nextAction |<br></div><div>table animateMoveFrom: cardContainer<br></div><div>to: startContainer<br></div><div>moving: cardMorph<br></div><div>next: nextAction ]<br></div><div> ]<br></div><div>^^^===^^^===^^^===^^^<br></div><div><br></div><div>So it is worthwhile to learn about closures (IMHO).<br></div><div><br></div><div>HTH,<br></div><div>-KenD<br></div><div><br></div><div><br></div><div><br></div></blockquote><div><br></div></body></html>