Introduction to the Io Programming Language


Author: Nicolaas A. Scheltens

CPS 499-03: Emerging Languages, Spring 2017


Key language concepts in Io

  • Pure object-oriented language: everything is an object.
  • Prototype-based object model; classes and instances are indistinguishable.
  • Objects communicate through messages.
  • Uses the Actor model of concurrency.
  • Influenced by Smalltalk.
  • No pointers.


Objects

Everything in Io is a object: integers, booleans, floats, and messages. All entities are objects that can be passed to other objects through messages, which are also objects.
  • there are no pointers, or all object have distinct pointers.
  • all created objects are called prototypes or instances.
  • all objects contain a list of slots or key/value pairs.
  • one root of the inheritance hierarchy with the name Object.
Example new prototype creation:
Io> Animal := Object clone
==> Animal_0x1e95638
  type = "Animal"
All new prototypes create a new type with the values being the name of the object. Instances inherit the type of the prototype from which they where cloned.

When an object receives a message, it searches its slots for a match. If no match is found, then the object sends the message to its prototype. If a message is received, and it does not have the correct slot, and none of its prototypes contain the correct slot, then an error is thrown. This is called dynamic binding.
  • All prototypes are stored in the Lobby object.
  • Methods can be assigned to object slots like any other assignment.


Lists and Maps

Io has two types of collections: list and maps.
Io> Alist := list("some","things")
==> list("some","things")
The following are useful list slots.
  • Alist size: returns size
  • Alist append("more"): adds the value to the end of the list.
  • list(1, 2, 3) average: averages integers or floats.
  • list(1, 2, 3) sum: sums integers or floats.
  • Alist at(1): return element at value.
  • Alist pop: removes last element in list, returns element.
  • Alist prepend("A"): adds element at the front of the list.
  • Alist isEmpty: returns true or false if the list is empty.
Maps, on the other hand, are built by cloning the map object.
  • add elements by using atPut(key, value).
  • get a value by using at(key).
  • asObjects: returns the map as slots.
  • asList: returns the map as a list of lists.
  • keys: returns the keys of the map in a list.


Concurrency

Io uses the Actor model of concurrency. Each actor is modeled as a coroutine.
Io> AObject waitRoutine := method(wait(3);"A done waiting - " print)
==> method(
wait(3);writeln("done waiting")
)
Io> BObject waitRoutine := method(wait(2);"B done waiting - " print)
==> method(
wait(2);writeln("done waiting")
)
Io> AObject @@waitRoutine;BObject @@waitRoutine; wait(4);"Main" print
B done waiting - A done waiting - Main
==> nil
The program above creates two objects and starts both. Even though AObject is declared first, BObject still finishes before AObject because we started each (approximately) at the same time.
  • the @@ operator creates an actor that will run the following object.
  • yield can be used to halt an actor.
  • actors will automatically yield after they have sent a message.


A Synchronization Barrier

    Barrier := Object clone
    Barrier clientNum := 0
    Barrier clientList := List clone
    Barrier clientArrive := method(clientName, 
       clientList append(call sender)
       "#{clientName} has arrived at the barrier" interpolate println
       clientNum = clientNum - 1
       if(clientNum == 0, done)
    )
    Barrier done := method(clientList foreach(v, v leaving()))
       
    Client := Object clone
    Client cnum ::= 0
    Client ready := method(
       wait(2)
       Wild 
       Barrier clientArrive("Client#{cnum}" interpolate)
    )
    Client Wild := method(if(cnum%2 == 0, wait(Random value(3))))
    Client leaving := method("Client#{cnum} is leaving the barrier" interpolate println)
    
    "Enter how many clients" println
    snums := File standardInput readLine
    nums := snums asNumber
    Barrier clientNum = nums
    
    for(i,1,nums,
       iClient := Client clone setCnum(i)
       iClient @ready()
       "Started a client"println
    )
    Coroutine currentCoroutine pause
    


Matrix Multiplication

    Mat := Object clone
    MatMult := Object clone
    ProcessFile := Object clone
    f := File with("Matrix.txt")
    f openForReading
    
    ProcessFile inList := f readLines
    ProcessFile build := method(
       for(a, 0, (inList size) - 1,
          if(a == 0, 
             s := inList at(a) split
             if(s at(1) != s at(3), Exception raise("Can not Multiply"))
             Mat height = s at(0) asNumber
             Mat width = s at(4) asNumber
             buildMat2
             builddList
          )
          if(a > 0 and a < (Mat height) + 1,
             s := inList at(a) split
             tList := List clone
             for(b, 1, (s size - 2), 
                tList append(s at(b) asNumber)
             )
             Mat mat1 append(tList)
          )
          if(a > (Mat height) + 1, 
             s := inList at(a) split
             for(c, 1, (s size - 2),
                Mat mat2 at(c - 1) append(s at(c) asNumber)
             )
          )
       )
    )
    ProcessFile buildMat2 := method(
       x := List clone
       for(d, 1, Mat width,
          x append(List clone)
       )
       Mat mat2 = x
    )
    ProcessFile builddList := method(
       for(e, 1, Mat height,
          Mat dList append(List clone) 
          for(f, 1, Mat width,
             Mat dList at(e-1) append(f)
          )
       )
    )
    
    Mat height := 2
    Mat width := 2
    Mat mat1 := List clone
    Mat mat2 := List clone
    Mat dList := List clone
    Mat placeAtd := method(h, w, v, dList atPut(h, dList at(h) atPut(w, v)))
    
    MatMult getNum := method(alist, blist,
       x := 0;
       for(i, 1, alist size,
          x = x + ((alist at(i-1)) * (blist at(i-1)));
          x println;
        )
    )
    MatMult placeAt := method(h, w,
       wait(2) 
       v := getNum(Mat mat1 at(h), Mat mat2 at(w))
       Mat placeAtd(h, w, v)
    )
    
    ProcessFile build
    Mat mat1 println
    Mat mat2 println
    
    for(i, 1, Mat height,
       for(b, 1, Mat width,
          nMatMult := MatMult clone
          nMatMult @placeAt(i-1, b-1);
          "starting" println
       )
    )
    while(Scheduler yieldingCoros size > 1, yield)
    f close
    Mat dList println
    
The following is an example of an input file for the prior program.
3 3 x 3 3
[ 1 2 3 ]
[ 4 5 6 ]
[ 7 8 9 ]

[ 10 11 12 ]
[ 13 14 15 ]
[ 16 17 18 ]


Reflection

In Io, any object/class can be expanded or modified by the programmer.
  • The following expression overwrites the print slot.
    Object print = "im printing something"
    
  • Due to its uniform syntax, there is little to no syntactic sugar in Io.
  • The OperatorTable is an object that contains all operators within the language.
  • All objects and their associated slots can be displayed by entering them into the interpreter.
  • New operators can be added using the addOperator message.

  • Example:
    Io> OperatorTable addOperator("!", 9)
    ...
    9   | !
    10  && and
    11  or ||
    ...
    Io> ! = method(bool, if(bool, false, true))
    ==> method(bool,
       if(!bool, true, false)
    )
    Io> ! true
    ==> false
    Io> ! false
    ==> true
    


Useful Links & Resources


Exercises

The following are some programming exercises that incorporate some essential Io concepts:
  • Create a prototype that reads a string of numbers from standard input and transforms that string into a list of numbers. Create an exponent slot that accepts two integers as input and returns the first number raised to the second number power. Send the exponent message to every integer in the newly-created list using the last integer as the exponent and print the modified list.

    Examples:
    Io> 1 2 3 4 5 2
    ==> list(1, 4, 9, 25)
    Io> 2 3 7 1 5 3
    ==> list(16, 81, 2401, 1, 625)
    
  • Use coroutines and the Actor model to create a synchronization barrier. Read the number of workers to be spawned as well as the capacity of the barrier as an integer from standard input. The barrier must wait for all workers to arrive before allowing any workers through the barrier.

    Examples:
    Io> 3
    Started a worker.
    Started a worker.
    Started a worker.
    Worker2 has arrived at the barrier.
    Worker1 has arrived at the barrier.
    Worker3 has arrived at the barrier.
    Worker2 is through the barrier.
    Worker1 is through the barrier.
    Worker3 is through the barrier.
    


References


Return Home