Friday, January 14, 2011

Setting Attributes

One test of dynamic languages is to try and set attribute values on an object dynamically (e.g., without knowing until runtime which attributes need to be set). Below, we compare a simple example in Python, a fairly dynamic language, to Factor:

class Foo(object):
    a, b, c = None, None, None

obj = Foo()

d = { "a" : 1, "b" : 2 }
obj.a = d.get("a")
obj.b = d.get("b")
obj.c = d.get("c")

print obj.a # 1
print obj.b # 2
print obj.c # None

We might directly translate the previous example to Factor code, using slot accessors to set attributes on the tuple instance:

TUPLE: foo a b c ;

foo new

H{ { "a" 1 } { "b" 2 } } {
    [ "a" swap at >>a ]
    [ "b" swap at >>b ] 
    [ "c" swap at >>c ] 
} cleave

[ a>> . ] [ b>> . ] [ c>> . ] tri

But, it's much better if you don't need to know ahead of time which attributes a class has (i.e., needing to write code to handle each attribute). In Python, you might instead set each value dynamically using the setattr function:

for name, value in d.items():
    setattr(obj, name, value)

We can use the set-slot-named word from the db.types vocabulary to do the same from Factor:

USING: assocs db.types fry kernel ;

: set-slots ( assoc obj -- )
    '[ swap _ set-slot-named ] assoc-each ;
Note: the set-slot-named word (and the offset-of-slot word that it uses) should probably be moved to the slots vocabulary.

We can simplify the previous example using our newly created set-slots word and try it in the Factor listener:

( scratchpad ) TUPLE: foo a b c ;

( scratchpad ) foo new

( scratchpad ) H{ { "a" 1 } { "b" 2 } } over set-slots .
T{ foo { a 1 } { b 2 } }

1 comment:

kobi said...

Very nice! I did something similar, a while ago.
probably slower since it uses mirror's introspection.

http://paste.factorcode.org/paste?id=1068