8.31.2006
5:22 AM

In a belated response to netytan, I post my own any? and all? written in Scheme.

(define (any? proc ls)
    (if (null? ls)
        #f
        (if (proc (car ls))
            #t
            (any? proc (cdr ls)))))

;; (any? even? '(1 3 5 8 7)) => #t, shortcircuiting at 8
;; (any? odd?  '(2 4 6 8 10)) => #f

(define (all? proc ls)
    (if (null? ls)
        #t
        (if (proc (car ls))
            (all? proc (cdr ls))
            #f)))

;; (all? even? '(2 4 6 1 2 3)) => #f, shortcircuiting at 1
;; (all? odd?  '(1 3 5 7 9 11)) => #t

Just because.


8.25.2006
9:03 AM

I've just spent a bit of time working with Syntax, a Ruby module made for doing syntax highlighting. I figured that since Io syntax was so simple, it would take little effort to make it work for me. Well, hey, I was right. Here's a little example of the results:

List docSlot("splat(*names)", "Assign one value from the list to each given symbol, starting from index 0.")
List splat := method(
    names := call message arguments map(arg, arg name)
    if(names size < size,
        Exception raise("Too many values to unpack")
    )
    if (names size > size,
        Exception raise("Need more than #io{size} values to unpack" interpolate)
    )
    0 to(size - 1) foreach(i,
        call sender setSlot(names at(i), at(i))
    )
    self
)

List => := List getSlot("splat")

fibo := method(n,
    a := 0
    b := 1
    
    0 to(n - 1) foreach(i,
        list(b, a + b) => (a, b)
    )
    a
)

fibo(10) println // 55

And here's the implementation:

require 'syntax'
require 'syntax/convertors/html'

class IoTokenizer < Syntax::Tokenizer
    IO_IMPORTANT = %w{
        and
        Block block
        call clone
        do
        getSlot
        List list
        Map message method
        newSlot not Number
        Object or
        print println
        self Sequence setSlot
        to
        with write writeln
    }
    
    def scan_string
        start_region :string
        loop do
            chunk = scan_until(/"/)
            if chunk[-2] and chunk[-2].chr == "\\"
                append chunk
            else
                append chunk[0..-2]
                break
            end
        end
        end_region :string
    end
    
    def step
        if comment = scan(/(#|\/\/).*#{EOL}/)
            start_group :comment, comment
        elsif comment = scan(/(\/\*).*?(\*\/)/)
            start_group :comment, comment
        elsif scan(/"""/)
            start_group :punctuation, '"""'
            start_group :string, scan_until(/"""/)[0..-4]
            start_group :punctuation, '"""'
        elsif scan(/"/)
            start_group :punctuation, "\""
            scan_string
            start_group :punctuation, "\""
        elsif digits = scan(/\d+(\.\d+)?(e\d+)?/)
            start_group :digits, digits
        elsif punc = scan(/[\(\)\[\]\{\}]|=|:=|;/)
            start_group :punctuation, punc
        elsif word = scan(/[A-Z][a-zA-Z_:\.]+/)
            if IO_IMPORTANT.include? word
                start_group :"important upcase", word
            else
                start_group :upcase, word
            end
        elsif word = scan(/[a-zA-Z_:\.]+/)
            if IO_IMPORTANT.include? word
                start_group :important, word
            else
                start_group :normal, word
            end
        elsif operator = scan(/[!@$%^&*+|\-]+/)
            start_group :operator, operator
        elsif
            start_group :normal, getch
        end
    end
end

Syntax::SYNTAX["io"] = IoTokenizer


8.22.2006
4:25 AM

Mixins define a set of methods that can be "mixed in" to a class definition in order to provide additional functionality. Of course, this often leads to another construct, such as the Ruby Module construct. But Io isn't Ruby.

We don't need a seperate construct for classes, and we certainly don't need a seperate construct for mixins. You can define your mixins just like any other objects, like this one, a clone of the Comparable module from Ruby:

Comparable := Object clone do(
    == := method(other,
        self compare(other) == 0
    )
    < := method(other,
        self compare(other) == -1
    )
    > := method(other,
        self compare(other) == 1
    )
    <= := method(other,
        self compare(other) != 1
    )
    >= := method(other,
        self compare(other) != -1
    )
    isBetween := method(this, that,
        (self > this) and (self < that)
    )
    removeAllProtos  # Remove Object from the mixin. It's going to cause trouble.
)

This mixin provides a number of comparison operators when the target object provides the "compare" method. Now, we just have to make sure that our mixin is on the top of our prototypes list, and therefore first in line for slot lookups. This example creates an object called SizeMatters, which inherits from Sequence, and then prepends the Comparable mixin to its prototypes:

SizeMatters := Sequence clone do(
    prependProto(Comparable)     # Here we prepend Comparable, putting it
                                 # first in line for lookups.
    compare := method(other,
        size compare(other size)
    )
    
    with := method(str,
        str clone setProto(SizeMatters)
    )
)

We can see this working here:

s1 := SizeMatters with("z")
s2 := SizeMatters with("yy")
s3 := SizeMatters with("xxx")
s4 := SizeMatters with("wwww")
s5 := SizeMatters with("vvvvv")

s1 < s2              // true
s4 == s5             // false
s4 isBetween(s1, s3) // false
s4 isBetween(s3, s5) // true

Io uses a form of multiple inheritence to accomplish this. Your new SizeMatters object inherits from Comparable first, and Sequence second. But since Sequence will provide most of the functionality for SizeMatters, we want to list it first, and so we do. Very dynamic.

Oh, and it's not only easier to understand. It also means that you can insert inheritence wherever you want, like in an already-existing object:

Number protos == list(Object)             // true
Number do(
    == := getSlot("==")                   // Overridding Number == is bad.
) prependProto(Comparable)                // Shove Comparable into the dependencies.
Number protos == list(Comparable, Object) // true

4 isBetween(2, 5) println                 // true

Now, isn't that nice? Ruby modules without Modules.


8.06.2006
5:23 AM

I've given a lot of thought to the use of lexical scopes and shadowing slots in Io, and I've come up with a bit of a monster that throws itself against the problem until it breaks every vertebrae. His name is LexicalScopeOperator.

LexicalScopeContainer := Object clone do(
    clone := getSlot("clone")
    init := method(.setSlot("call", call sender call))
    forward := method(call delegateTo(self call sender))
    doMessage := getSlot("doMessage")
    methodList := list("setSlotWithType", "updateSlot", "getSlot", "hasLocalSlot",
        "hasSlot", "ancestorWithSlot", "removeSlot", "setSlot")
    genericMethod := method(name,
        Protos getSlot("Block") clone setArgumentNames(list("name")) setMessage(
            "if(name in(self call shadowed),
                call delegateToMethod(self, \".#io{name}\")
            ,
                call delegateTo(self call sender)
            )" interpolate asMessage
        ) setIsActivatable(true)
    )
    methodList foreach(mthd,
        setSlot("." .. mthd, getSlot(mthd))
    )
    methodList foreach(mthd,
        .setSlot(mthd, genericMethod(mthd))
    )
    list("genericMethod", "methodList") foreach(var, .removeSlot(var))
    removeAllProtos
)

The original code was much longer; I opted for some meta-programming in order to get rid of some nasty repetition. genericMethod generates the needed functions, and hides them each in ".functionName". So, "getSlot" is stored in ".getSlot", and "getSlot" is replaced by a new method. These new methods check to see if the named slot is "shadowed" by the lexical scope, and if not, delegates again out to "self call sender". Everything else is delegated out. Of course, you probably have no idea what you could possibly use this thing for, or what it does, for that matter. So I introduce to you let.

Now, let comes from Lisp. It creates a new lexical scope with a given list of symbols and values, and lets those symbols represent those values within that scope only. On top of that, any closures created within the let would grab the value from inside of the let and keep it afterwards.

(setf 'x 1)
(print x)     ;; 1
(let ((x 2))
    (print x) ;; 2
)
(print x)     ;; 1

It doesn't do this by doing any ugly temporary slot sets like the functions I posted a few days ago. Lisp's variable lookup is quite different from Io's, afterall. So I hacked at it, and came up with the monster from above. The let I came up with that uses LexicalScopeContainer is defined like this:

let := method(
    shadowing := call message arguments slice(0, -1)
    body := call message arguments last
        
    container := LexicalScopeContainer clone
    
    call shadowed := list

    shadowing foreach(msg,
        if(msg name == "setSlot",
            call shadowed append(call sender doMessage(msg arguments first))
            container doMessage(msg)
        ,
            call shadowed append(msg name)
        )
    )
    
    container doMessage(body)
)

And it is used like this:

x := 1
x println     // 1
let(x := 2,
    x println // 2
)
x println     // 1

All blocks defined also work properly:

let(x := 2,
    f := block(x + 1)
)
f println     // 3

I also decided to redefine expand in these terms:

List expand := method(
    call shadowed := call message arguments slice(0, -1) map(name)
    body := call message arguments last
    
    container := LexicalScopeContainer clone
    
    call shadowed foreach(i, name,
        container .setSlot(name, at(i))
    )
    
    container doMessage(body)
)

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

x     println // 0

Of course, I am working on a way to refine the beast, as it is rather voodoo-magical. But it certainly works better than the other two solutions I came up with on the way. I'll share those tomorrow.


8.03.2006
7:40 AM

Over at Pinupgeek's blog you'll find something very interesting: a Python->Io bridge! I can't test it myself as it isn't Windows-compatible (nor is Io at the moment for that matter), but please, do take a look.


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.


8.01.2006
8:20 AM

Some important functional ... functions!

List foldl := method(
    accu := first
    args := list(nil)
    if(call message arguments size == 1,
        meth := call message argAt(0) name
        slice(1) foreach(i, accu = accu performWithArgList(meth, args atPut(0, i)))
    ,
        fn := Protos getSlot("Block") clone setArgumentNames(call message arguments slice(0, 2) map(name)) setMessage(call argAt(2)) setScope(call sender)
        slice(1) foreach(i, accu = fn(accu, i))
    )
    accu
)

List foldr := method(
    accu := last
    args := list(nil)
    if(call message arguments size == 1,
        meth := call message argAt(0) name
        slice(0, -1) reverseForeach(i, accu = i performWithArgList(meth, args atPut(0, accu)))
    ,
        fn := Protos getSlot("Block") clone setArgumentNames(call message arguments slice(0, 2) map(name)) setMessage(call argAt(2)) setScope(call sender)
        slice(0, -1) reverseForeach(i, accu = fn(i, accu))
    )
    accu
)

list(1, 2, 3) foldl(l, r, l + r) println // ((1 + 2) + 3) = 6
list(1, 2, 3) foldl(+) println

list(2, 3, 5) foldr(l, r, l * r) println // (2 * (3 * 5)) = 30
list(2, 3, 5) foldr(*)           println

list(6, 2, 1) foldl(l, r, l - r) println // ((6 - 2) - 1) = 3
list(6, 2, 1) foldl(-)           println

list(6, 2, 1) foldr(l, r, l - r) println // (6 - (2 - 1)) = 5
list(6, 2, 1) foldr(-)           println

list(list, 1, 2, 3) foldl(l, r, l push(r)) println // list push(1) push(2) push(3) = list(1, 2, 3)
list(list, 1, 2, 3) foldl(push)            println

list(42) foldr(+) println // (42) = 42

This is mostly to demonstrate the flexibility of Io's message manipulation, and method construction. The functions have two forms: an explicit and an implicit. The explicit form takes two symbols and a message, constructing a function to do the folding. The implicit form simply takes a symbol which it uses as the name of the function on the appropriate value to be called.

Next time I'll hit you up with some macro goodness using activatable messages.