8.03.2006
6:05 AM

The list method expand is a flow control construct designed to make it easier to deal with multiple return values stashed in a list without having to explicitly unpack it.

List expand := method(
    argNames := call message arguments slice(0, -1) map(name)
    body := call message arguments last
    temp := Object clone
    sender := call sender
    
    argNames foreach(i, name,
        temp setSlot(name, sender getSlot(name))
        sender setSlot(name, at(i))
    )
    
    result := call sender doMessage(body)
    
    argNames foreach(name,
        sender setSlot(name, temp getSlot(name))
    )
    
    result
)

x := 0

list(1, 2, 3) expand(x, y, z,
    x println // 1
    y println // 2
    z println // 3
    x + y + z
)     println // 6

x println     // 0

Notice that even though I didn't create a new block, the variables are restored upon returning. This should cover most cases. Edge cases created by trying to access sender slots with the same names as the passed arguments will fail.

The next method is seperate, which has two forms. The first form is the shorthand form, which takes one argument. This is a message to pass to each item on the list. If the message returns true, the item is put into the "passed" list. Otherwise, it is put into the "failed" list. The second form takes a symbol and message as its arguments, setting that slot to the current item and then performing the message.

List seperate := method(
    passed := list ; failed := list
    if(call message arguments size == 1,
        meth := call argAt(0)
        foreach(e,
            if(e doMessage(meth, call sender),
                passed append(e)
            ,
                failed append(e)
            )
        )
        list(passed, failed)
    ,
        argName := call argAt(0) name
        stored := call sender getSlot(argName)
        foreach(e,
            call sender setSlot(argName, e)
            if(call sender doMessage(call message arguments last),
                passed append(e)
            ,
                failed append(e)
            )
        )
        list(passed, failed)
    )
)

list(1, 2, 3, 4, 5) seperate(< 3) expand(lt, gt,
    lt println // list(1, 2)
    gt println // list(3, 4, 5)
)

list(1, 2, 3, 4, 5) seperate(x, x < 3) expand(lt, gt,
    lt println // list(1, 2)
    gt println // list(3, 4, 5)
)

This last one is a new implementation of sortBy, which also comes in two forms. The shortcut form is my much preferred one because of its brevity, but I figured I couldn't include just the one form if I was going to stay true to the rest of my methods (and Io's methods, really). The first form simply takes a message to do in the context of each item. The second form takes two argument names and a message to do in the context of the sender.

List sortBy := method(
    if(size == 0, list,
        if(call message arguments size == 1,       
            meth := call argAt(0)

            x := first doMessage(meth)            
            slice(1) seperate(doMessage(meth) < x) expand(lt, gt,
                call delegateTo(lt) append(first) appendSeq(call delegateTo(gt))
            )
        ,
            arg1 := call argAt(0) name
            arg2 := call argAt(1) name
            msg  := call argAt(2)
            
            temp1 := call sender getSlot(arg1)
            temp2 := call sender getSlot(arg2)
            
            call sender setSlot(arg2, first)
            
            slice(1) seperate(e,
                call sender setSlot(arg1, e)
                call sender doMessage(msg)
            ) expand(lt, gt,
                call sender setSlot(arg1, temp1)
                call sender setSlot(arg2, temp2)
                call delegateTo(lt) append(first) appendSeq(call delegateTo(gt))
            )
        )
    )
)

list("Hello", "Gel", "Foobar") sortBy(size) println                  // list("Gel", "Hello", "Foobar")
list("Hello", "Gel", "Foobar") sortBy(x, y, x size < y size) println // list("Gel", "Hello", "Foobar")

That about wraps it up for tonight. Tomorrow I should be doing some more work on the EasyParser library, so stay tuned. Also, the macros thing is a no-go, because Io has stopped compiling on Windows, and that particular feature isn't in the build I'm using. Ah well. Hopefully we'll be able to compile in Windows again once the new build setup is ready for us.