Io Language: Metaprogramming


    Metaprogramming in io



    In the continuation of a series of articles about the wonderful language io, I was going to write about metaprogramming.
    habrahabr.ru/blogs/crazydev/29375
    habrahabr.ru/blogs/crazydev/28254
    habrahabr.ru/blogs/crazydev/28167
    habrahabr.ru/blogs/crazydev/28041

    Metaprogramming is the creation of programs that create other programs as a result of their work (or, as a special case, changing or supplementing themselves during execution).
    © Orthodox Wikipedia

    The canonical meaning of the term “metaprogramming” is completely uninteresting; any Turing-complete language with IO libraries is capable of generating source codes for programs in any language. Much more interesting is the possibility of self-knowledge of the program - introspection, as well as the generation and modification of itself in the process of execution.

    If you look at the godfathers io, you can see Lisp, Smalltalk, Self and other dynamic languages. But they all solve the metaprogramming problem in their own way, for example, Lisp generates code during compilation from macros, very powerful pieces. Smalltalk contains an advanced system of "reflection" (reflection) that allows you to walk through the tree of objects in any direction. Io followed the path of smalltalk and builds its dynamic mechanisms based on the object model, rather than syntax versatility (like a lisp).


    Introspection


    To explore objects, io provides a number of methods that allow you to do all sorts of funny things, for example, the slotNames method. If you send a message to slotNames to an object, you can get a list of its slots. Well, for example:
        Io> Lobby slotNames
        ==> list ("exit", "Protos", "forward", "args", "Lobby", "launchPath")
      

    As you can see, the method returns a normal list, on which you can crawl with foreach and other maps. Well, in principle, do whatever your heart desires.
    In the same way, you can get a list of object protocols (protos method):

        Io> Lobby protos
        ==> list (Object_0x4191a0)
      


    An even more interesting method is getSlot, which allows you to take the value of a slot without actually calling it. This alignment allows you to create new methods in runtime based on the old ones (of course, you can change them on the fly and get completely different methods). And if you think a little more, you can use this feature to create pseudo-lazy structures:

        Io> SampleObject: = Object clone
        ==> SampleObject_0x4461c0:
        type = "SampleObject"
        Io> SampleObject m: = method ("Look ma, i can fly!" Println)
        ==> method (
        "Look ma, i can fly!" println
        )
        Io> SampleObject anotherM: = SampleObject getSlot ("m")
        ==> method (
        "Look ma, i can fly!" println
        )
        Io> SampleObject anotherM
        Look ma, i can fly!
        ==> Look ma, i can fly!
        Io>
      

    And a completely unearthly code method. It allows you to get the source code of any method in a normalized form:

        Io> List getSlot ("map") code
        ==> method (setSlot ("aList", List clone);
        setSlot ("a1", call argAt (0));
        if (a1 == (nil), Exception raise ("missing argument"));
        setSlot ("a2", call argAt (1));
        setSlot ("a3", call argAt (2));
        if (a2 == (nil), self foreach (v, setSlot ("ss", stopStatus (setSlot ("c", a1 doInContext (getSlot ("v"), call sender))));
        if (ss isReturn, ss return (getSlot ("c")));
        if (ss stopLooping, break);
        if (ss isContinue, continue);
        aList append (getSlot ("c")));
        return (aList));
        if (a3 == (nil), setSlot ("a1", a1 name);
        self foreach (v, call sender setSlot (a1, getSlot ("v"));
        setSlot ("ss", stopStatus (setSlot ("c", a2 doInContext (call sender, call sender))));
        if (ss isReturn, ss return (getSlot ("c")));
        if (ss stopLooping, break);
        if (ss isContinue, continue);
        aList append (getSlot ("c")));
        return (aList));
        setSlot ("a1", a1 name);
        setSlot ("a2", a2 name);
        self foreach (i, v, call sender setSlot (a1, i);
        call sender setSlot (a2, getSlot ("v"));
        setSlot ("ss", stopStatus (setSlot ("c", a3 doInContext (call sender, call sender))));
        if (ss isReturn, ss return (getSlot ("c")));
        if (ss stopLooping, break);
        if (ss isContinue, continue);
        aList append (getSlot ("c")));
        return (aList))
      

    Actually using these uncomplicated methods, you can make a lot of business to get anywhere in runtime.


    Modification


    Any method in io can be overloaded in a simple way, back to io's singleton:

        Singleton: = Object clone
        Singleton clone: ​​= Singleton
      

    The standard clone method of any object creates a copy of it (if roughly), and in this case, the clone method, being overloaded, returns an existing instance of the object, which is exactly what one would expect from a singleton.
    Any object in io can be expanded, modified and introspect unlimitedly.
    (In general, half a year ago, when I started writing an article about metaprogramming, we lrrr and oleganza built a sql generator that converted normal method calls in terms of io syntax into a string with an sql query. It looked something like this:

        SQLGen select (*) from t_some_table t where t.id = 42
      

    The result was a sql string. But I sowed the source, and these two ghouls said that it wasn’t of special value (:
    So no one was upset.


    Well, that’s probably all. You can write a lot more, but it’s better to dig through the manuals on the official website: www.iolanguage .com / docs .
    ps It turned out to be crumpled, my head hurts, Monday after the release. Laziness, hell and death. Sorry. But it's better than nothing at all (:

    Also popular now: