Custom exception types can be implemented by simply cloning an existing Exception type:
MyErrorType := Error clone
Primitives are objects built into Io whose methods are typically implemented in C and store some hidden data in their instances. For example, the Number primitive has a double precision floating point number as it's hidden data and it’s methods that do arithmetic operations are C functions. All Io primitives inherit from the Object prototype and are mutable. That is, their methods can be changed. The reference docs contain more info on primitives.
This document is not meant as a reference manual, but an overview of the base primitives and bindings is provided here to give the user a jump start and a feel for what is available and where to look in the reference documentation for further details.
The ? Operator
Sometimes it's desirable to conditionally call a method only if it exists (to avoid raising an exception). Example:
if(obj getSlot("foo"), obj foo)
Putting a "?" before a message has the same effect:
obj ?foo
A List is an array of references and supports all the standard array manipulation and enumeration methods. Examples:
Create an empty list:
a := List clone
Create a list of arbitrary objects using the list() method:
a := list(33, "a")
Append an item:
a append(“b”)
==> list(33, “a”, “b”)
Get the list size:
a size
==> 3
Get the item at a given index (List indexes begin at zero):
a at(1)
==> "a"
Note: List indexes begin at zero and nil is returned if the accessed index doesn’t exist.
Set the item at a given index:
a atPut(2, "foo")
==> list(33, "a", "foo", "b")
a atPut(6, "Fred")
==> Exception: index out of bounds
Remove an item at a given index:
a remove(“foo”)
==> list(33, "a", "b")
Inserting an item at a given index:
a atPut(2, "foo")
==> list(33, "a", "foo", "56")
foreach
The foreach, map and select methods can be used in three forms:
Io> a := list(65, 21, 122)
In the first form, the first argument is used as an index variable, the second as a value variable and the 3rd as the expression to evaluate for each value.
Io> a foreach(i, v, write(i, “:”, v, “, ”))
==> 0:65, 1:21, 2:122,
The second form removes the index argument:
Io> a foreach(v, v println)
==> 65
21
122
The third form removes the value argument and simply sends the expression as a message to each value:
Io> a foreach(println)
==> 65
21
122
map and select
Io's map and select (known as filter in some other languages) methods allow arbitrary expressions as the map/select predicates.
Io> numbers := list(1, 2, 3, 4, 5, 6)
Io> numbers select(isOdd)
==> list(1, 3, 5)
Io> numbers select(x, x isOdd)
==> list(1, 3, 5)
Io> numbers select(i, x, x isOdd)
==> list(1, 3, 5)
Io> numbers map(x, x*2)
==> list(2, 4, 6, 8, 10, 12)
Io> numbers map(i, x, x+i)
==> list(1, 3, 5, 7, 9, 11)
Io> numbers map(*3)
==> list(3, 6, 9, 12, 15, 18)
The map and select methods return new lists. To do the same operations in-place, you can use selectInPlace() and mapInPlace() methods.
In Io, an immutable Sequence is called a Symbol and a mutable Sequence is the equivalent of a Buffer or String. Literal strings(ones that appear in source code surrounded by quotes) are Symbols. Mutable operations cannot be performed on Symbols, but one can make mutable copy of a Symbol calling it’s asMutable method and then perform the mutation operations on the copy.
Common string operations
Getting the length of a string:
"abc" size
==> 3
Checking if a string contains a substring:
"apples" containsSeq("ppl")
==> true
Getting the character (byte) at position N:
"Kavi" at(1)
==> 97
Slicing:
"Kirikuro" slice(0, 2)
==> "Ki"
"Kirikuro" slice(-2) # NOT: slice(-2, 0)!
==> "ro"
Io> "Kirikuro" slice(0, -2)
# "Kiriku"
Stripping whitespace:
" abc " adMutable strip
==> "abc"
" abc " asMutable lstrip
==> "abc "
" abc " asMutable rstrip
==> " abc"
Converting to upper/lowercase:
"Kavi" asUppercase
==> "KAVI"
"Kavi" asLowercase
==> "kavi"
Splitting a string:
"the quick brown fox" split
==> list("the", "quick", "brown", "fox")
Splitting by others character is possible as well.
"a few good men" split("e")
==> list("a f", "w good m", "n")
Converting to number:
"13" asNumber
==> 13
"a13" asNumber
==> nil
String interpolation:
name := "Fred"
==> Fred
"My name is #{name}" interpolate
==> My name is Fred
Interpolate will eval anything with #{} as Io code in the local context. The code may include loops or anything else but needs to return an object that responds to asString.
A range is a container containing a start and an end point, and instructions on how to get from the start, to the end. Using Ranges is often convenient when creating large lists of sequential data as they can be easily converted to lists, or as a replacement for the for() method.
The Range protocol
Each object that can be used in Ranges needs to implement a "nextInSequence" method which takes a single optional argument (the number of items to skip in the sequence of objects), and return the next item after that skip value. The default skip value is 1. The skip value of 0 is undefined. An example:
Number nextInSequence := method(skipVal,
if(skipVal isNil, skipVal = 1)
self + skipVal
)
With this method on Number (it’s already there in the standard libraries), you can then use Numbers in Ranges, as demonstrated below:
1 to(5) foreach(v, v println)
The above will print 1 through 5, each on its own line.
Tthe methods openForAppending, openForReading, or openForUpdating are used for opening files. To erase an existing file before opening a new open, the remove method can be used. Example:
f := File with(“foo.txt)
f remove
f openForUpdating
f write(“hello world!”)
f close
Creating a directory object:
dir := Directory with(“/Users/steve/”)
Get a list of file objects for all the files in a directory:
files := dir files
==> list(File_0x820c40, File_0x820c40, ...)
Get a list of both the file and directory objects in a directory:
items := Directory items
==> list(Directory_0x8446b0, File_0x820c40, ...)
items at(4) name
==> DarkSide-0.0.1 # a directory name
Setting a Directory object to a certain directory and using it:
root := Directory clone setPath("c:/")
==> Directory_0x8637b8
root fileNames
==> list("AUTOEXEC.BAT", "boot.ini", "CONFIG.SYS", ...)
Testing for existance:
Directory clone setPath("q:/") exists
==> false
Getthing the current working directory:
Directory currentWorkingDirectory
==> “/cygdrive/c/lang/IoFull-Cygwin-2006-04-20”
Creating a new date instance:
d := Date clone
Setting it to the current date/time:
d now
Getting the date/time as a number, in seconds:
Date now asNumber
==> 1147198509.417114
Date now asNumber
==> 1147198512.33313
Getting individual parts of a Date object:
d := Date now
==> 2006-05-09 21:53:03 EST
d
==> 2006-05-09 21:53:03 EST
d year
==> 2006
d month
==> 5
d day
==> 9
d hour
==> 21
d minute
==> 53
d second
==> 3.747125
Find how long it takes to execute some code:
Date cpuSecondsToRun(100000 repeat(1+1))
==> 0.02