So you're fundamentally misunderstanding how methods and blocks work, but that's ok. Let's go over the basics:
- Methods are blocks who activate when you call them by name, and whose scope is set to nil. When we talk about scope.
- Blocks have their scope set to the context in which they're created.
context means a locals object, basically a stack frame. Scope means who will be the "sender" of the block activation when the block/method is invoked. You can access this by the call sender
object inside the context of a method or block.
Now, let's look at your code. It's almost perfect, there's only one thing missing, and it's non-obvious.
Since methods have dynamic scope, their scope
message returns nil. This signifies to the evaluator that whichever object received that message, should be passed in as the sending context. We don't want that behaviour, we want to capture some scope, specifically the locals of the iter
method we've defined. Let's look at a corrected example:
List iterator := method(
idx := 0
itr := Object clone
itr next := method(
idx = idx + 1
at(idx)
) setScope(thisContext)
itr prev := method(
idx = idx - 1
at(idx)
) setScope(thisContext)
itr
)
I've simplified the bodies, but they haven't changed in terms of functionality (apart from a few less message sends). The important thing is the setScope
call passed to the method before assignment to next
/prev
. I could have chose to rewrite this as:
iter prev := block(
idx = idx - 1
at(idx)
) setIsActivatable(true)
but I'd have then had to make the block activatable, since blocks are not activatable by default. The code above, and the corrected itr prev
using method()
are functionally equivalent.
Methods are not closures, blocks are. A block is simply a method whose scope is non-nil, they are the same object.