Io> Ferris live
So week 2 brings me (a week late) to a language I’ve never heard of before. As Ruby was Mary Poppins, so Io is, apparently, Ferris Bueller. I have a poor familiarity with Ferris Beuller’s Day Off so here ends all references to Ferris for this week’s posts.
Io is an object based prototype language. Everything is an object that receives messages and how the object responds to the message is dictated by its prototype. If a given object cannot respond to a message based on its prototype then the its type’s prototype is consulted (and so on). This will become clearer in a moment. This is the analogy of inheritance in a prototype language.
Like Ruby, Io is an interpreted language. After installing the interpreter and supporting libraries, statements can be entered directly into the interpreter. To run programs from a file the interpreter can either be invoked on the command line or (if the host operating system supports this idiom) referenced at the start of the program file.
Running statements in the Io Interpreter
[dan@home ~]$ io Io 20090105 Io> "hello world" println hello world ==> hello world Io>
Running an Io Program by Invoking the Interpreter
[dan@home ~]$ vi testing.io [dan@home ~]$ io testing.io hello world
Running an Io Program by Specifying Interpreter in Source
[dan@home ~]$ echo '#!'$(which io) > testing2.io [dan@home ~]$ cat testing.io >> testing2.io [dan@home ~]$ chmod +x ./testing2.io [dan@home ~]$ ./testing2.io hello world
Grab the sources to Io from the Io Github repo and unzip them into a build directory. There are a number of libraries required in order to build Io and the packaged components. I’m running Fedora 15 and needed to install the following packages to get a build that issued no warnings.
Use the build.sh script in the extracted contents of the download to build and install Io:
[dan@home ~]$ ./build.sh [dan@home ~]$ sudo ./build.sh install [dan@home ~]$ sudo ./build.sh LinkInstall
Finally you will need to update the locations used for finding dynamically loaded libraries. On my Fedora system this requires adding a new file referencing /usr/local/lib into the /etc/ld.so.conf.d/ directory and re-running ldconfig:
[dan@home ~]$ sudo echo "/usr/local/lib" > /etc/ld.so.conf.d/usrlocal.conf [dan@home ~]$ sudo ldconfig
Given it’s youth, I found it surprising how much material is available on Io. A google for “io language” reveals quite a wealth of results.
Primary amongst these resources is Io’s home at http://www.iolanguage.com/. There are links from there to reference material and a useful wiki. From the wiki there are links to a useful page of samples and a style guide.
And as a sign that the language has surely “arrived”, Stackoverflow has an iolanguage question tag.
Io is a strong but dynamically typed language. By adding slots we can provide implementations of expected methods. We can see this with a simple example:
[dan@home ~]$ io Io 20090105 Io> 1 + 1 ==> 2 Io> 1 + "two" Exception: argument 0 to method '+' must be a Number, not a 'Sequence' --------- message '+' in 'Command Line' on line 1 Io> "one" + "two" Exception: Io Assertion 'operation not valid on non-number encodings' --------- message '+' in 'Command Line' on line 1 Io> "one" type ==> Sequence Io> Sequence + := method(next, self .. next) ==> method(next, self .. next ) Io> "one" + "two" ==> onetwo Io> "one" + 1 ==> one1 Io> 1 + "one" Exception: argument 0 to method '+' must be a Number, not a 'Sequence' --------- message '+' in 'Command Line' on line 1 Io>
Here we send the + message to two Number objects and we get the expected result. When we try this between a Number and Sequence we get an exception and ditto when we try it with two Sequences. Once we define a + against Sequence our tests work. Sort of. I included that final erroring case as it highlights that the message is going to a Sequence and not a Number. Adding a + to Number such that it would accept a Sequence proved a little beyond me at this stage.
Apart from nil and false it seems pretty much everything in Io evaluates to true.
Io> true and true ==> true Io> true and false ==> false Io> true and nil ==> false Io> true and 1 ==> true Io> true and 0 ==> true Io> true and "" ==> true Io> true and "true" ==> true Io> true and "false" ==> true Io> true not ==> false Io>
The slotNames message enables us to see what slots are available on a given object:
Io> Object slotNames ==> list(newSlot, ownsSlots, isError, foreachSlot, currentCoro, <, removeAllSlots, list, for, doString, uniqueHexId, clone, become, write, evalArgAndReturnNil, serializedSlots, isNil, method, block, pause, isActivatable, deprecatedWarning, isLaunchScript, coroWith, evalArg, uniqueId, ?, actorProcessQueue, do, in, setProto, super, writeln, setSlot, !=, inlineMethod, doRelativeFile, removeAllProtos, coroDo, asyncSend, continue, stopStatus, ancestorWithSlot, print, protos, evalArgAndReturnSelf, actorRun, not, type, and, return, break, slotSummary, >, message, ==, serialized, slotNames, ifNonNilEval, asSimpleString, hasLocalSlot, while, updateSlot, switch, perform, returnIfError, asString, hasSlot, try, returnIfNonNil, hasProto, prependProto, getSlot, wait, justSerialized, hasDirtySlot, thisContext, removeProto, appendProto, println, lazySlot, loop, slotDescriptionMap, launchFile, .., relativeDoFile, serializedSlotsWithNames, compare, , yield, setSlotWithType, init, resend, isTrue, lexicalDo, or, doFile, argIsActivationRecord, raiseIfError, ancestors, isIdenticalTo, ifNil, ifNilEval, performWithArgList, cloneWithoutInit, contextWithSlot, thisLocalContext, >=, if, isKindOf, memorySize, <=, ifNonNil, coroFor, thisMessage, apropos, @, getLocalSlot, ifError, markClean, coroDoLater, slotValues, -, doMessage, proto, setIsActivatable, futureSend, removeSlot, shallowCopy, handleActorException, @@, setProtos, argIsCall) Io> Person := Object clone ==> Person_0x82f9b60: type = "Person" Io> Person slotNames ==> list(type)
The proto will list all the slots available to an object via its prototype.
A Note on Assignment Operators
Io has 3 operators that play a similar role:
The first is assignment to an existing slot. An exception is raised if the slot is not present. This stackoverflow answer deals nicely with the difference between the second two.