Beginner's Guide to Io

  1. About Io and the book
  2. Getting Started
  3. Your First Program
  4. Basic Components of Io Programs
  5. Objects

Addons and You

Compression

Encryption

Networking

Server

Io Language Pages

About This Wiki

This work is disjointly licensed under the GNU FDL, the CC BY SA, and Io's license.

The authors of this tutorial are:

  • Daniel Ehrenberg (LittleDan)
  • Samuel A. Falvo II
  • Olle Jonsson (just a note on String concatenation)

And many others who wish to remain anonymous.

Hello world

When learning to program, the first thing usually learned is how to write a program that prints "Hello, world!" to the console. It gives programmers a feel of how simple programs are structured. Io has many ways to write things to the console, so at times it can be confusing about which one to use. The one that's most often used is the write function. Enter the Io's interactive interpreter (by typing io at the console) and type the following:

write("Hello, world!\n")

With the interactive interpreter, this should look like:

user@computer:~$ io
Io> write("Hello, world!\n")
Hello, world!
==> nil

Another way would be:

"Hello, world!\n" print

Some of this makes sense. It looks like write is telling Io to write something to the console, specifically the stuff after it. The stuff after it is in quotes so it's some text, and it's what's going to be printed. But why are those parentheses there, what's with that \n at the end of it, and why was there an arrow with the quoted text after it?

The parentheses are telling write to use the thing within the parentheses, which is called an argument. write is called a function (remember functions in math?). There might be multiple arguments within the parentheses separated by commas in a single function. The first and only argument for write, namely "Hello, world!\n", is a string (because it's a string of letters).

The \n at the end of the string is an escape sequence. You can tell that because it begins with a backslash. This particular escape sequence signals a newline, the equivalent of pressing enter. If you just pressed enter in the middle of the string (or at least this type of string -- we'll get to that later), it would confuse Io.

The thing where there's an arrow and then the string that was the argument of the write function is the value that write returned. Every function returns a value. With some functions, it makes sense to return something (such as a function to add two numbers), but with other functions, it just returns something simple done with the arguments. In this case, write returns the special value nil, indicating it has nothing to return.

Concatenate Strings

Use the double dot operator to concatenate strings. Or, you can use string interpolation, as shown below.

Let's say olle is a Person instance, and it has both a first name and a last name:

Io> olle
==> Object_0080B818 do(
  appendProto(Object_00806B58)
  lastname := "Jonsson"
  title := "Developer"
  firstname := "Olle"
)

We want a method to show first and last name with a space in-between:

Io> olle fullname := method(firstname .. " " .. lastname)
==> method(firstname ..(" ") ..(lastname))

or, using string interpolation:

Io> olle fullname := method("#{firstname} #{lastname}" interpolate)
==> method("#{firstname} #{lastname}" interpolate)

Now, calling olle fullname will yield "Olle Jonsson". Looking at the returned method object, we can see how the Io interpreter creates regular message calls (the parentheses) for the method ..().

So, this is the new olle instance:

Io> olle
==> Object_0080B818 do(
  appendProto(Object_00806B58)
  fullname := Block_007B5030
  lastname := "Jonsson"
  title := "Developer"
  firstname := "Olle"
)

(Note: only this instance has the fullname method. You could add the fullname method to Person instead, which would make it available to olle via olle's prototype, Person. Also, the firstname and lastname and title could also be moved up to the Person. But this is the way pieces of example code are.)

Simple arithmetic

In Io, you can use arithmetic expressions and they will work correctly. Arithmetic in Io is just like functions, except it uses objects to help. Objects make it so that instead of typing something like +(1, 2), you can type 1+2. More about objects later. + is still a function, though, so it returns a value. This is very useful; it makes it so you can use Io as a simple calculator. Notes about what it's doing are put after two slashes (like this: //).

Io> 1+2 //addition
==> 3

Io> 4-5 //subtraction
==> -1

Io> 7*3 //multiplication
==> 21

Io> 3/6 //division
==> 0.50000

Io> 2**3 //exponents
==> 8

Io> 7%2 //remainder of 7/2 (technically, 7 mod 2)
==> 1

This follows normal order of operations (called precedence) and parentheses can be used. As you'd expect, you can use write on numbers, but the newline isn't included. You have to use multiple arguments with write to print multiple things and then it returns all of those things put together. So to print (1/3)**2, you would write:

write((1/3)**2, "\n")

As you would expect, this prints 0.11111. It returns the string "0.11111\n" because with the write function, multiple arguments are converted to strings and joined together. Then that value is printed and returned.

Variables

A variable is basically a word that stands for a value. They are somewhat like variables in mathematics, except in mathematics, variables could only be numbers and they were one letter long. In programming, functions, objects, strings, and numbers are all types of variables, but we haven't defined any yet. Many variables, such as write, 3, and "Hello, world!" can already be used, but only some of them can be changed. You can make your own variables using = and :=. Below are some examples of making and using variables:

x := 3
line := "\n"
write(x, line)

Can you tell what's happening? The variable x is being set to 3 and the variable line is being set to "\n", which is equivalent to a newline. Then, the contents x and line are being written to the console. Since x is 3 and line is a newline, this prints 3 and then goes to the next line on the console. The function returns "3\n".

In Io, there is a difference between creating and changing the value of variables. If a variable doesn't exist yet, you have to use :=, but if you've already given it an initial value using :=, you can use = for subsequent definitions. Here's an example:

x := 1
incrementor := 2
write("x is ", x, "\n")
x = x + incrementor
write("but when we added ", incrementor, " to it, it became ", x, "\n")

It may be confusing that sometimes we use := and other times we use =, but you'll get used to it. If you want to, you can always use :=, but when we get into objects later, it will become very inconvenient to keep using :=, so you should probably start using = whenever appropriate.

Programs

Until now, you've been simply been going to Io's interactive interpreter. This prevents you from making larger applications or writing things for others. If you want to write code to be reused, simply put it in a text file and run it with io <filename>, where <filename> is the name of the file you use. Here's an example of using a file on Linux to store a program:

user@computer:~$ cat > incrementor.io
x := 1
incrementor := 2
writeln("x is ", x)
x = x + incrementor
writeln("but when we added ", incrementor, " to it, it became ", x)
user@computer:~$ io incrementor.io
x is 1
but when we added 2 to it, it became 3
user@computer:~$

That was the last example we just did. If you noticed, I used a .io file extension. This is in no way mandated, it is merely a convention. Something happened differently this time then when we did it from a file, if you noticed. Unlike before, we didn't see what each function returned. Instead, we only saw what was explicitly output by write.

If you're on Linux or a similar system (such as Unix, Mac OS X, or Cygwin), you can make it so that your file can be run simply by typing ./yourProgram.io (where yourProgram.io is the name of your program), without needing to precede it with io. This can be accomplished by putting a line of code at the beginning of your program and then giving it executable permissions. On Linux, that code is:

#! /usr/bin/env io

It may differ for other systems. To give it executable permissions, simply type the following:

chmod +x yourProgram.io

Again, this may be different on different systems. Once you do this, nothing will change about the actual execution of the code, but you can, for example, just double click on a source code file in a GUI and it will run.

Writing functions

As I said before, functions are just another type of variable, so you can create them using := and change them using =. Functions themselves are created with a function called method. Here's an example of a function:

add := method(a, b, //function to add 2 numbers
  a + b
)
writeln(add(1, 2)) //writes 3
x := 1
writeln(add(3, x)) //writes 4
x = add(4, 5) //9
writeln(x) //writes 9
x = add(x, 1) //in effect increment x by 1
writeln(x) //writes 10

The function add takes two arguments, a and b, and then returns a + b. It would do exactly the same thing if, instead of a and b, we used the variable names 'this' and 'that', everywhere that a and b were used. In that case, the function would be written as:

add := method(this, that,
  this + that
)

What a function really does is take a list of arguments; then it assigns the arguments to local variables, which are specified when you make the function. Local variables stay in the function you're working in and you can't get to them outside of the function. In the most recent case, those local variables are 'this' and 'that', so the variable 'this' will take the first argument, and the variable 'that' will take the second argument. Then it executes the rest of the function and returns the result. The function can have multiple lines of code and the last line will be the value returned. Here's an example:

examineArgs := method(this, that,
  writeln("This is ", this, ".")
  writeln("That is ", that, ".")
)
// usage:
examineArgs(3, 5)
/* writes:
This is 3.
That is 5. */
x := examineArgs("hi", "bye")
/* writes:
This is hi.
That is bye. */
write(x)
/* writes:
That is bye. */

Why was x set to "That is bye.\n"? Because that's what examineArgs returned. It returned the value of the last line, the value of write("That is ", that, ".\n"). That was set to "bye", so it printed the string "That is bye.\n" and returned the same string.

If you want to return something before the function ends, you can use the return function. Unlike most functions, you don't need to put parentheses around the argument to call the return function. Here is an example of its usage:

returning := method(this, that,
  writeln(this)
  return this
  writeln(that)
)
x := returning(1, 2)
//writes "1\n"
write(x) //writes 1 with no newline

As you can see, the code after return isn't used at all. Right now, this doesn't seem very useful, but later, when we get into flow control, it will be used very often.

Conditionals

Conditionals in Io are made using the if function:

if(a == 1) then(
  writeln("a is one")
) else(
  writeln("a is not one")
)

However, the preferred way to write this (without the need for then() and else() messages, so it is faster):

if(a == 1, writeln("a is one"), writeln("a is not one"))