The beauty of Io

Io — An exclamation of joy or triumph; Webster dict. (1913)

quote from original Io Programming Guide 2003.

Looking back to 2003 when I worked with Io Programming Language and even did some network core patch and made Russian translation.

I was looking for a good light language which fits my style — simple, but powerful, something like Smalltalk, but more accessible, ideally open sourced. There were no NodeJS and Javascript was not that popular like now. Ruby only started its growth, there were even no Rails yet. Java was already popular as a good enterprise language and nice replacement to Delphi and C++. But for good dynamic language the choice was not clear. Then I met the Io the language invented by Steve Dekorte and it was a great hope.

Io is a programming language focused on expressiveness through simplicity.

pure, dynamic, concurrent, accessible

The concurrency was already there in 2003, but some API was in active development yet.

Of course the promising Io was very attractive for me and I was pleased with its ideas. Comparing to Smalltalk it didn't have a Smalltalk-like VM and Code Browser and was intended to be used as a script-language using text files which is more practical for some production usage especially for small tasks. It's so small and accessible that you can simply install it and run your custom script everywhere. Steve was talking about transitive persistence when a state is restored by applying a set of transactions rather than memory snapshot-based persistence like in Smalltalk.

I liked the idea of prototypes in Io which was cleaner than in Javascript. In JavaScript prototype support is ugly and limited. It's not obvious how to use them. But in Io protos were real like in Self Language . Self was experimental prototype-based language made by Sun Microsystems and inspired by Smalltalk. While the Self was experimental its VM was then used in Java HotSpotVM. In Io you're able to add as much prototypes to the object as you like. Prototype-based approach resolves many object-oriented issues in very convenient and logical way.

I used Io in production as embedded language for some structures generation tool. Yet it was lack performance for such a dynamic language back in days. Today there is a fast Javascript runtime, not sure about Io performance, but computers are faster today, and it's good to give Io a new try.

I decided to give Io a short review now in 2016 with today's tendencies language can be still a good fit for some scripting or embedded solutions. Besides that its concepts of purity can be applied in other languages.

Let's go straight into some examples.

Io in 2 minutes

First, I encorouge you to checkout Io Tutorial which is super small and reads like a breeze.

In Io everything is an object be it custom Person object or Number or String. You can send messages to objects and receive a result.

Run io with the io command in terminal:

> io
Io 20140919  

We can send round message to number with fraction digits and get integer:

12.2 round  
==> 12

Messages can be chained. Here we send now message to Date object followed by year message which gives the year of current date in the end:

Date now year  
==> 2016

You create new custom objects by cloning:

person := Object clone  
==>  Object_0x7ffeb5246390:

Then object can be customized by assigning values to its slots. Think of slot as a key in hash:

person name := "Vladimir"  
==> Vladimir

We can also add a method to the object:

person welcome := method(writeln("Welcome, ", self name))  
==> method(
    writeln("Welcome, ", self name)
)

Now when we send welcome message to our person object we get welcome message printed on the screen:

person welcome  
Welcome, Vladimir  

Let's rename the person to vladimir just by assigning it to a new slot:

vladimir := person  
==>  Object_0x7ffeb5246390:
  +                = method(anotherPerson, ...)
  name             = "Vladimir"
  welcome          = method(...)

vladimir and person are now both referring to the same object.

But how about creating another person? Io is prototype-based, not class-based, so what we need is just to clone the object into another one:

john := vladimir clone  
==>  Object_0x7ffeb2ea7610:

We customize john cloned from vladimir with its name:

john name := "John"  
==> John

But do the john has ability to respond to welcome message? Yes:

john welcome  
Welcome, John  

But how it's done? Well john is cloned from vladimir so the clone dispatches its messages through its prototype which is vladimir so it works fine with Io.

Interesting that Io supports some big numbers that wouldn't fit some primitive 64-bit numbers in some languages. Here we take 128th power of 2 and get quite a big number:

(2 ** 128) asString(0, 0)
==> 340282366920938463463374607431768211456

Operators are also message sends behind the scenes You can introduce your own operators, but you need to use special method for assigning a slot with an operator name:

person setSlot("+", method(anotherPerson, list(self, anotherPerson)))  
==> method(anotherPerson, 
    list(self, anotherPerson)
)

This method above sums two persons into a list, so we can use it like this:

twoPeople := vladimir + john  
==> list( Object_0x7ffeb5246390:
  +                = method(anotherPerson, ...)
  name             = "Vladimir"
  welcome          = method(...)
,  Object_0x7ffeb2ea7610:
  name             = "John"
)

Do you know a language where it's so simple to do as in Io?

How about classes? Well just rename person to Person in the begining:

Person := Object clone  
==>  Object_0x7ffeb5246390:

Now think of Person as a class from which you can clone some new objects.

Let's move on to some real-world example.

Decision Making System built with Io

In this example I want to build small app which will recognize volatility for a currency. It will use third-party currency rates API to determine latest rate with the rate in the past and decide if rate increased too much with a volatily resolution.

Checkout full source on Github - https://github.com/livin/io-volatility.

Here's what the app will tell you when you run it:

USD/RUB Volatility Analysis  
Today: 73.11  
Last Month: 67.52  
Growth/Loss: 8%  
Resolution: It's quite volatile!  

Let's look how it's made.

First we import json.io module:

doFile("json.io")  

Then we've some definitions for our base/target currency. We'll check USD/RUB rates:

baseCurrency := "USD"  
targetCurrency := "RUB"  

Next we figure out dates that we will check rates for. We'll use today's date and 30 days ago. For this we need to calculate 30 days ago date:

_30days := Duration clone setDays(30)  
lastMonth := Date now - _30days  

Duration is an object which is used for time durations/periods and we looking for 30 days period. Second line here is the most interesting and is not available in many modern languages like Java, but is available in Io. We substract 30 days duration from now (today's date). In Java your can't override operators, in Io it's just another message send to the Date object.

Next step is to define some methods to access currency rates public API. I came across internet and found http://fixer.io as a simple FX Rates exchange API. Here's sample request/response of the API:

GET http://api.fixer.io/latest?base=USD&symbols=RUB  
{
  "base":"USD",
  "date":"2016-01-04",
  "rates": { "RUB":73.109 }
}

Now we've got the idea of the API. Let's provide a method to access it:

rateForDate := method(base, currency, aDate,  
  URL with("http://api.fixer.io/#{aDate asString(\"%Y-%m-%d\")}?base=#{base}&symbols=#{currency}" interpolate) fetch parseJson rates doString(currency)
)

Here is what we do. We define method called rateForDate with base currency, target currency and a date argument. In contents of the method we create an URL object with the URL string built from given parameters. To build proper URL string we use interpolate method of the String object which will inline all expressions inside #{} block. Once the URL with proper string is built we send fetch message to the URL to receive data from given endpoint. Once data is fetched it's returned as a content in form of string. Then we send parseJson message to the contents which parses response and returns Io object structure from the JSON content. We'll have an object like this:

==>  JsonObject_0x7ffeb2d2cc10:
  base             = "USD"
  date             = "2016-01-04"
  rates            = JsonObject_0x7ffeb2d492f0

So we've now JSON represented as an Io object and traversible with message sends in Io, like json rates RUB will give 73.109.

Last part of the method is to get the actual rate with sending rates followed by doString(currency) which will be equivalent to rates RUB sent to the JsonObject.

Here's how we can use that method:

rate := rateForDate("USD", "RUB", Date now)  

Similarly we've a method to get latests today's rates called rateLatest.

Then we actually do fetch rates and do calculations:

// Rate fetch & calculation
rateToday := rateLatest(baseCurrency, targetCurrency)  
rateLastMonth := rateForDate(baseCurrency, targetCurrency, lastMonth)  
growth := (((rateToday / rateLastMonth) - 1.0))  
growthInPercent := (growth * 100) round  
growthAbs := growth abs  

See how nicely we use round and abs messages to calculate round and abs values. This way calculation is done and growth is our most important metric to make a decision. Next goes the most incredible part of our super decision making software. Here's it:

// Resolving volatility
if(growthAbs > 0.2) then(resolution := "Panic!!! Enormous volatility.")  
  elseif(growthAbs > 0.05) then(resolution := "It's quite volatile!")
  elseif(growthAbs > 0.01) then(resolution := "No volatility. Okay.")
  else(resolution := "Perfect! No volatility at all!")

It's just a set of if/then/else branching to decide on volatility. If currency rate incresed over 20% to the last month value then it's high volatility. Over 5% is still quite volatile currency. If it's below 5% we assume it's not that volatile. And if it's less than 1% then it has no volatility at all. If you know better simple algorithm to determine volatily let me know.

In the end we just print-out our results.

This way we've just built amazing decision making system with Io and it was seamless. The core objects like Date is just enough and perfect to do such things like dates substructions which is not that simple in other langs. You fetch contents from any endpoint with just fetch message to the URL object. The extensibility allows to add behavior to existing objects like String to add extra functionality - use json.io library and you get a parsed JSON object by sending parseJson message to String.

The Smalltalk-inspired message-sending mechanisms & extensibility makes just so easy to think and design your program. All objects are working together by sending messages. It's just literally smalltalk.

Prototypes vs Classes

photo: Daisuke Takakura

What is advantage of prototypes over classes your would say? Well it is not that clear. Even if you try to search for advantages of prototype-based vs. class-based programming languges you will not find any obvious explanation, but rather some scientific explanation of the differences.

To understand the difference and advantages you must better try it in action.

When you need to create one object in class-based lang what do you do? Of course you need to create a class first. Even a small object in class-based programming language requires a class definition. In prototype-based language it's much simpler - you need an object - go for it:

creator := Object clone  
creator name := "Vladimir Livin"  
creator email := "vladimir@outofearth.spc"  

So in prototype-based language creating a single object requires less thinking and mental stops. If you need to create similar objects you can clone from a generic prototype later.

This also resolves the issue with singleton pattern — you don't need to create a class with private constructor and implement a static single instance accessor. You just create your singleton object once.

Second is inheritance & multiple inheritance in particular. Do you know a feeling when you need an object to have the behavior of class C, but it's already extended from class B. In C++ in's done with multiple inheritance, in Io you can just add another object as prototype. Better to see on example:

First we define a Person prototype:

Person := Object clone  
Person welcome := method(writeln("Hello, ", self name))  

This defines a welcome behavior for the Person prototype. Now let's create our person john:

john := Person clone  
john name := "John D"  
john welcome // prints "Hello, John D"  

Person functionality works pretty fine. Now imagine we want to add employee functionality. Then we create another prototype Employee:

Employee := Object clone  
Employee monthlySalary := method(self rate * self hoursWorked)  

The main responsibility of Employee prototype (or think of it as a class) is to calculate the monthlySalary based on the hourly rate and hours worked. Now in order for john to have this behavior we just need to add Employee prototype to john's protos list:

john appendProto(Employee)  

And we also need to set some employee-specific data, and voila he's now an employee, who can get to know his monthly salary:

john rate := 15  
john hoursWorked := 160  
john monthlySalary // ==> 2400  

Multiple inheritance is not a silver bullet, but sometimes it's the only solution to the problem and with prototypes it's much easier.

There are several other ways how you can play with prototypes. I encourouge you to try it yourself and share with me if you found it useful.

Last, but not least, prototypes is what makes language incredibly simple and unified. If you missed it yet - In Io you don't have prototypes as another kind of entities - prototypes just as all other usual objects - there is no difference between prototype and an object. In class-based languages you have 2 different kind entitites: classes, objects (instances of classes). In prototype-based language you have only one kind entitity: objects. This greatly simplifies and unifies language and I would argue that leads to reduce your mental stops when designing.

Tribute to Io

Do you know a feeling when you work with new language or framework and you feel that you need to check how it works behind the scenes or change something? I have very bad feeling regarding this with some langs and frameworks.

But Io stands out.

If I miss something in the guide I look into Io core sources. Both .io and .c sources are very clear and you can easily figure out how something is working. This is rare in today's open source world when you open some source just to figure out that it's just a big wrapper around another ugly part of the source. So I give Io the simply-hackable award.

Conclusion

Io will definitely supercharge you with ideas of simplicity, dynamism and prototype-based programming. Yet it's practical that you can go ahead install it and use immediately. If you don't want it to be your main language yet — there are still good stuff to learn from it.

There are lot of stuff I didn't cover in the article: server-side Io, actors/coroutines, actual embedding & bindings, package manager, web-frameworks.

Io recently featured in "Seven Languages in Seven Weeks" a book by Bruce Tate released on Pragmatic Programming Bookshelf. Get some inspiration from his quote:

Seven Languages in Seven Weeks book cover image

Just as each new spoken language can make you smarter and increase your options, each programming language increases your mental tool kit, adding new abstractions you can throw at each new problem. Knowledge is power. The Seven in Seven series builds on that power across many different dimensions. Each chapter in each book walks you through some nontrivial problem with each language, or database, or web server. These books take commitment to read, but their impact can be profound.

In the end I want to thank Daisuke Takakura for photo credit. His work 'Monodramatic' is beatiful and inspiring I encourouge you to check-out.

And of course thanks to Steve & community around Io. Checkout Io on Github.

References:
1. Io Programming Language
2. Io Language Tutorial.
3. Github: Io.
4. My old Russian translation of Io Programming Guide.
5. Simple Currency Analysis System In Io - GitHub.
6. Seven Languages in Seven Weeks: A Pragmatic Guide to Learning Programming Languages by Bruce A. Tate
7. CNN: 'Monodramatic': Daisuke Takakura's mesmerizing clone world
8. Monodramatic by Daisuke Takakura.