Methods and blocks

...

Consider the following code. It creates a MagicCard object, then attempts to add five methods dynamically: isRed, isBlue, etc, all based on the Colors list. This introduces a scoping issue.

Colors := list(
    list("U", "Blue"),
    list("W", "White"),
    list("B", "Black"),
    list("R", "Red"),
    list("G", "Green")
)

MagicCard := Object clone do(
    type := "MagicCard"
    name := ""
    cost := ""
)

#
# create is* methods dynamically:

Colors foreach(pair,
    symbol := pair at(0)
    name := pair at(1)
    createColorMethod := method(symbolx, namex,
        MagicCard setSlot("is" .. namex, block(
            call target cost containsSeq(symbolx)
        ))
    )
    createColorMethod(symbol, name)
)
removeSlot("createColorMethod")

Note that inside createColorMethod, we use a block. From inside the block, we can see the name symbolx. These scoping rules seem to work much like Python.

If we would use a method instead, symbolx would not be visible at that point. (At this point, I'm not sure if there is a different way to access it. Experiments with the call object didn't give the desired result.)

Changing scopes

As seen on the mailing list:

Steve DeKorte: "In Io you can change a Block to a Method or vice versa simply by sending a setScope() message."

Also:

"In Io, you can take a arbitrary block and turn it into a method and vice versa:"

foo := block(a, b = 1; a+b) // arbitrary block
foo = getSlot("foo") setScope(Nil) // now it's method

foo := method(a, b = 1; a+b) // arbitrary method
foo = getSlot("foo") setScope(SomeObject) // now it's block

Note that the b = 1 is not a keyword argument! Rather, these methods/blocks have one argument (a) and a body that rebinds b and then returns a + b. The way I understand it, it works like this. As a block, it looks for b in the enclosing scope. As a method, it looks for it in the current object and parent objects.

Locals

Messages in a method are by default sent to a Locals clone. So, a call to a message foo inside a method is not the same as self foo! This is why the following difference occurs (as seen in newsgroup):

method(getSlot("Number"))
# returns nil

method(IoVM getSlot("Number"))
method(Object getSlot("Number"))
method(self getSlot("Number"))
# all return 0

SainTiss: "Any message in a method is by default sent to a Locals clone. Since Locals does not have a proto, it will never reach IoVM in the proto chain."

(Also note that method(Number) does work. Explanation: "Well, by default a message is sent to Locals, but if Locals does not respond to it (as with "Number"), it is sent to self instead. So the difference is that Locals responds to getSlot, but not to Number.")