Io language notes
Io is a small, well-designed and efficient scripting language created in 2002 by Steve Dekorte. Io is particularily pleasant because it has a rather clean, regular syntax that frees us from the ugly C-style braces and trailing semicolons, pretty much like an hybrid of Python and Lisp. Io also offers very high level language traits that condense much of the advances made in scripting years since the 90s.
Io notably introduces some interesting perspectives and features such as prototype-based inhertiance, asynchronous execution operator, or full reflectivity.
Io language main site is available at http://www.iolanguage.com, Steve's page is available at http://www.dekorte.com. There is also the main Io wiki available at http://io.kicks-ass.net/FrontPage. !! Server not responding? !!
Related pages:
- QuestionsAndAnswers, questions and answers that came on the Io mailing list
- Gotchas?, a page that tries to collect common gotchas for Io newbies
- PrototypesVsClasses, compares the prototype-based inheritance to the class-based inheritance
- ObjcBridge, introduces the bridge that allows cross-integration of Io and Objective-C.
- ModuleSystem, a draft for a module/package system for Io
- DocumentationSystem, a draft for a documentation system for Io
- IoUnit, a simple automated testing framework for Io
1. A quick glance at Io
Here is an Object-oriented "Hello World" written in Io:
HelloObject = Object clone
HelloObject sayHello = method(
"Hello World!" print
)
HelloObject sayHello()
You can check out some more Io examples here [1]
2. Io language traits
Now that the impatient was fed with some source code, I will introduce Io from a more theorical point of view and expose the traits (a.k.a. the language features) that make it such a pleasant beast:
- Prototype-based inheritance: Inheritance in IO is realised by cloning objects and maintaining a dynamic relation between the clone and the original.
- Purely Object-Oriented: all entities that are manipulated in Io derive from the same Object prototype.
- Garbage-collected: objects are automatically allocated and deallocated.
- Dynamically typed: although Io has a notion of type, variables are untyped and operations prototypes do not carry type information. Some type checks can be made using generally available methods.
- Strongly typed: all interactions are through messages to object. Memory is never accessed directly.
- Interpreted: Io source code is not compiled to bytecode but rather read and converted to an in-memory representation.
- Code as data: blocks of code are reified into objects, which can be modified and dynamically created.
- Reflective: as a consequence of the "code as data", it is possible to investigate the properties of any Io object at runtime, including its source code.
- Dynamic: because code is data (and vice-versa), it is possible to change a program logic during its execution. It is of course also possible to dynamically modify an object attributes and operations at runtime. Combined with the prototype-based inheritance, this allows to dynamically "change an object class".
Although Io shares some traits with functional languages, Io could be considered as a member of the imperative family of languages. However, Io dynamicity and deep reflectivity enables programming styles that are not usually possible with other languages from the imperative family.
I consider Io as a very interesting language for scripting existing applications, while I would rather advice the use of a statically, strongly typed language for the application core. Io has a simple syntax, is very flexible and powerful, but lacks some features of more mature languages, such as Python:
- Io does not offer a module/package system
- There is no code documentation tool (e.g. JavaDoc?, POD, PyDoc?, etc)
- There are no type-checking/object utilities (interfaces)
Io is constently evoloving, and Steve is rather open to comments, as proved the improvement of the VM toward a single-rooted object hierarchy (primitives used to inherit from IoValue?, and Object was a primitive) undergone in June 2003. In this respect, this small disadvantages will be surely be resolved in the future. In the meantime, Io is really fun to play with, and is a perfect language to be embedded in an application (especially Objective-C apps ;).
3. Io terminology
To have a better understanding of the different terms encountered in Io documentation and in the Io mailing list, here is a short list of terms with their accompanying definition:
- Prototype: a prototype is an object that can be cloned. The link between a clone and its prototype is very close to the link between a class and an instance. Clones will resolve attributes and operations that are not locally defined in their prototype. If this sounds abstract, you can think of prototypes as classes that you could create and manipulate just as other objects.
- Slot: a slot is an object part that allows to bind values to names. Slots can be considered as a name --> value mapping belonging to a particular object. Attributes and operations are first resolved in an object slot.
- Actor: First introduced by Carl Hewitt in his PLANNER paper of 1969, the concept of an actor is equivalent to an active object (thread) that would asynchronously process received messages (like operations). In Gul Agha actor model, messages are first queued in a FIFO attached to the object, and sequentially processed.
- Asynchronous message: when an object (sender) sends a message to another object (receiver), the sender usually waits for the sender to receive the message, process it and send a response (indicating the message was processed, optionally with a return value). This is called synchronous communication, when sending is a blocking act for the sender. With asynchronous messages, the sender simply sends the message, but do not wait for the response, enable it to continue to act while the message is processed by the receiver.
- Future: also know as "promise" a Future is an encapsulation of the future response message (actually response value) to an asynchronous message. A future enables objects to reference a value that is not yet computed.
Note: Maybe also exlain continuation, scope, resolution, tag, typed and untyped, reflectivity, etc.
Recommendend readings:
- Reactive Objects by Johan Nordlander
- Lambda the Ultimate
4. Io object model
Io is a purely object-oriented language, which means that everything is an object. Many object languages that derive from the C family are not able to manipulate primitive types (integers, floats, etc) consistently with object. Io has a single primitive: the Object. In this respect, the dynamic properties of objects are available for all entities in Io.
Definition of a message Prototype based Values, Primitives and Objects Mechanisms involved in responsing to a message Messaging model
Io has the very appreciable feature of accessing the object that sent a message to the receiving object. This allows to modify the behaviour of the receiving object according to the type of the invoking object. The sender object is accessible using the "sender" slot of the receiving object.
5. Understanding Io concepts
Io is based on some concepts that may not be familiar to people used to common programming languages such as C, C++ or Java. At first, Io is inspired by the following kinds of languages:
- Prototype-based language: languages that realise inheritance by cloning object rather than instanciating them from a class.
- Actor language: languages that consider objects to be dynamic and to communicate with messages
- Functional languages: languages that provide a lambda-like operator, either closure or blocks.
Don't be afraid if you don't know the languages or understand my short presentation, this will be explained here.
A. Types and Protoypes
Even if types and prototypes share the same "types" suffix, they both represent different things. A type qualifies and categories a given value, while a prototype is already a value.
Most programming languages allow to define new types ; in class-based languages, new types are defined with classes. Classes can be considered as a model that will be reproduced by the instances, the reproduction of the model is done by the class constructor operations. The main problem with classes is that they may not be referencable or accessible (if the language is not reflexive like C++, unlike Java), and that it is definitely not possible to modify them at runtime.
Prototypes allow to do the same as defining new types through classes: instead of describing a class, we construct an object that will serve as the actual model for future "instances". The equivalent operation to creating an instance is simply to "clone" the prototype. This language trait allows to freely manipulate prototypes, which is not always possible with classes (though a fully reflexive non-prototype language would certainly exhibit the same features).
The idea behind prototypes (which could also be called "stereotypes") is that you build a collection of predefined objects that will serve as bases for all objects that will be based on them. Prototype is a very interesting way to weave objects together.
A good thing would also be to have a look at this page [2].
B. Types and Tags
Tag in Io are inspired from Lua typing system [2], a tag specifies a given type. For instance, a Lua function could be either implemented in Lua or in C. To make the difference between both functions (which are considered as first-class language citizens, ie. values) the tag is used to indicate wether the function is in C or in Lua. Tags can then be considered as a sub-type.
Tags are useful for customising Io because they allow to extend or redefine the behaviour of a specific type, like for instance redefine how a list would react the add or remove messages.
Steve gives the following explaination on tags :
Every value is a primitive of some sort - a String, List, Objects, etc. And the data structure for each primitive begins with "mark" and "tag" members.
Example:
typedef struct { IoMark? mark; Tag *tag; ByteArray? *byteArray; } IoString?;
The mark is for garbage collection purposes and the tag is a reference the primitive's tag datastructure. The tag is used to figure out which C function to call for a given operation on a value. For example, when the garbage collector decides to free the IoString??, it ends up looking in the IoString??'s tag and finds the freeFunc function pointer and calls it. This function pointer is set to IoString?_free. The freeFunc function pointer in a tag for a different primitive would would be set to a different C function. This makes it easier for people to externally add new primitves.
Links:
- [2] See Lua 4.http://www.lua.org/manual/4.0/manual.html manual in sections 3 and 4, at http://www.lua.org/manual/4.0/manual.html. They provide a great
explaination on how tags can be used.
C. Reflectivity
Io is an intrinsically open language, as it offers very powerful reflexive features. One of the interesting features is that Io stores the whole program as a tree that mirrors the source code. This tree is not compiled to bytecode, but is rather directly executed.
One of the side effects of this choice is that you can get the source code at anytime. For instance, any Io object can output its source code. This is a truely interesting feature from an open-source development perspective, which recalls me the power of categories in Objective-C.
D. Language constructs reification
The combination of tags and reflexivity grants Io with very powerful self-modifications features, such as redefining the behaviour of executing a block value. Here is a Io code snippet that substitutes the default block activation function with a new one:
IoTag? *tag = IoState?_tagWithInitFunction_(ioState, StateTagInit?*)IoBlock?_initTagWithId_); tag->activationFunc = MyBlockActivationFunc?;
then in the activation function, you can do what you want:
IoValue? * MyBlockActivationFunc?(IoBlock? *self, IoObject? *target, IoObject? *locals, IoMessage? *m) { /* Do what I want */ /* And call the normal IoBlock? activation function */ IoBlock?_target_locals_call_(self, target, locals, m); }
[ Note that this example was given by Steve on the Io mailing-list]
I'm not sure if this is the right term for qualifying this, but to me, language reification means that the language internal mechanism is reified so as to be able to reprogram the language with the language itself (or through the use of simple extensions in C).
See http://www.wikipedia.org/wiki/Dynamic_typing
Comments
Feel free to add comments on this document.
In this line of code there's two closing parens but only one opening; is that right?:
IoTag? *tag = IoState?_tagWithInitFunction_(ioState, StateTagInit?*)IoBlock?_initTagWithId_);
-- BayleShanks?
