📜 ⬆️ ⬇️

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 - a special case - changing or complementing themselves during execution).
© Orthodox Wikipedia

The canonical meaning of the term “metaprogramming” is not interesting at all; any turing-complete language that has an IO library can generate source texts of programs in an arbitrary 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 of io, you can see Lisp, Smalltalk, Self, and other dynamic languages. But they all solve the problem of metaprogramming in their own way, for example Lisp generates code in the compilation process from macros, very powerful pieces. Smalltalk contains a developed system of "reflections" (reflection) allowing you to walk along the tree of objects in any direction. Io followed the path of smalltalk and builds its dynamic mechanisms on the basis of the object model, and not the universality of the syntax (like Lisp).


Introspection


For the study of 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 a slotNames 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 that can be crawled by foreach and other maps. Well, in principle, do what you want.
Similarly, 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 on 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 quite already unearthly method code. It allows you to get the source code of any method in the 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 straightforward methods you can tinker with business anywhere in runtime.


Modification


Any method in io can be overloaded in a simple way, let's return to the singleton on io:

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

The standard clone method of any object creates a copy of it (if it is rough), but 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 unlimitedly expanded, modified and introspected.
(Actually, half a year ago, when I started writing an article about metaprogramming, we used lrrr ' olegana and oleganza to create a sql generator that converted normal method calls from the syntax of io to a string with an sql query. It looked like this:

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

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


Well, that's all, I guess. You can write a lot more, but it's better to dig into the manuals on the official website: www.iolanguage.com/docs .
ps Crumpled up, headache, Monday after the release. Laziness, hell and death. Sorry. But better than nothing ((:

Source: https://habr.com/ru/post/50873/


All Articles