The Io Language
Io "is a dynamic prototype-based programming language." It seems more or less dead since 2010, basically since the book mentions it. Its site states "actively developed until 2008". While there are a few updates from last year, the latest available build for Windows dates to 4th of December 2013, more than ten years ago. Much of Io is written in C and one should be able to build it from source using cmake. While I like the retro character of plain C, similar to old Pascal, I am not experienced in the C ecosystem. I am using Windows and building C has been proven to be a hassle due to dependencies and compiler inconsistencies. So I went with the provided Windows build.
Io is prototype based, like JavaScript and looks different than your usual C-styled languages. The core library
iovm
comes with a unit testing framework. The UnitTest
is an old-school xUnit, e.g.FizzBuzzTest := UnitTest clone do ( setUp := method( super(setUp) self fizzbuzz := FizzBuzz clone ) // ... testFizz := method( result := fizzbuzz single(3) assertEquals("Fizz", result) ) // ... testTo5 := method( result := fizzbuzz upto(5) assertEquals(list("1", "2", "Fizz", "4", "Buzz"), result) ) )The corresponding Fizz Buzz is
FizzBuzz := Object clone do ( single := method(n, if (n % (3 * 5) == 0, "FizzBuzz", if (n % 5 == 0, "Buzz", if (n % 3 == 0, "Fizz", n asString))) ) upto := method(limit, Range 1 to(limit) map(n, single(n)) ) )This could be written more concise, but such was my first Io code. I should probably do a Prime Factors, too. It seems that I am obsessed with unit tests, as it is usually the first thing I look for in a new language, e.g. Scheme or Assembly. If you want to know more about Io, read the guide or the second chapter of Seven Languages in Seven Weeks. While the language is not developed further, it will change the way you see programs - exactly as Bruce promises.
Io Addons
The Io distribution contain a bunch of extensions collected under the IoLanguage GitHub organisation. Many extensions are wrappers around well tested C libraries, but these "may not be installed on your system already." And this is probably the reason why the Windows distribution only contains 28 out of the 80+ available addons. While I do not need MySQL or graphics library wrappers, basic functions like socket communication or regular expressions would be nice. There is Eerie, the Io package manager, which should be able to deal with compiling addons, but its installation fails on my system - because of a dependency that needs a missing C library (recursion ;-). So I try to add addons manually. It is the main purpose of this post to explain how it can be done.
Let me start with some general structure of Io addons: Installed or bundled addons are located in
%IO_HOME%\lib\io\addons
and share the same folder structure as expected by Io's AddonLoader
. Assume an addon named Foo
, then the following folders and files (could) exist:_build
: Several folders with the build results of C code, mostly empty._build\dll\libIoFoo.dll
andlibIoFoo.dll.a
: Compiled C code, its dynamic library and GCC import library file._build\headers
: Empty for bundled addons, but should contain the C headers of exported functions, i.e. theIo*.h
files fromsource
.bin
: Starter scripts for addons which are command line tools. e.g. Kano.io\*.io
: Main Io source code, i.e. new prototypes. There is at least aFoo.io
.samples\*.io
: Optional sample code.source\*.c|h
: C source code, at leastIoFooInit.c
. The empty folder must exist.tests\correctness\run.io
and one or more*Test.io
: Unit tests, therun.io
runs all tests in the current folder.tests\performance\*.io
: Optional performance tests, sometimes withrun.io
.depends
file: a list of other prototypes (or addons) this addon depends on.protos
file: a list of all prototypes this addon provides.
Foo
will find the addon and import it making all its prototypes available. This works for all provided prototypes given the manifest (i.e. the protos
file) is correct.Io Package Management: Eerie
While working with the different types of addons, see this and the next article, I seem to have reverse engineered much of Io's build and package infrastructure ;-). Knowing about it upfront would have helped. So Eerie is the package manager for Io. Unfortunately its installation failed on my system - and it seemed way too complicated for me because it created multiple environments, much like Python's
virtualenv
. Its documentation is "coming soon", and not helping. Some addons on GitHub are Eerie packages and miss necessary files like protos
. While sometimes these files are still there - an oversight as it seems, Eerie packages contain other files:package.json
is the Eerie manifest. It contains the description of the package and its dependencies. This file is useful.eerie.json
is some kind of manifest, used in the Eerie Package Storage Database. Only two addons have it and the database is empty.build.io
will "rewriteAddonBuilder
to represent a receipt for your package." Most addons have this and it contains information about needed native libraries and transitive dependencies.
PackageInstaller extractDataFromPackageJson()
method creates the protos
, depends
and build.io
files from package.json
. I will have to do this by hand. Unfortunately not all package.json
are well maintained and some miss important information.Now I explain the installation of several addons with different needs and increasing difficulty using concrete examples:
Addons without Any Native Code: Kano
There are some addons which do not have any C source, e.g.
CGI, ContinuedFraction, Rational
and integrations like Bitly, Facebook,
etc. All of these are included in the distribution. When looking for addons, I checked all addon repositories for ones without native code and found Docio, Eerie and Kano. I guess these were not included because they are part of Eerie. Let's look at Kano. Kano is a simple Make/Rake inspired tool for Io. It uses a Kanofile
to declare tasks. Let's get it running:> cd "%IO_HOME%\lib\io\addons" > git clone https://github.com/IoLanguage/kano Cloning into 'kano'... > io -e "Kano println" Exception: Object does not respond to 'kano'This does not work.
io -e "<expression>"
runs Io and passes the command, much like Ruby does. Passing the prototype name asks Io to load it and it is not found. By Io convention prototypes start with an uppercase letter while instances (and fields and variables) start with a lowercase one. Somewhere (in the source of Importer
maybe) I read that the names must match for importing. Also this is one of only three repositories which lowercase name, i.e. docio
, eerie
and kano
. Tip: Watch out for addon repository names matching the primary prototype.> ren kano Kano > io -e "Kano println" Exception: unable to read file '...\lib\io\addons\Kano\depends'Ah progress. It is missing the
depends
and protos
files I mentioned earlier. It has a package.json
instead:{ "version": 0.1, "author": "Josip Lisec", "description": "Rake-like utility for Io.", "readme": "README.textile", "category": "Utility", "dependencies": { }, "protos": ["Kano", "Namespace", "Namespaces", "ObjectDescriber"] }So no dependencies and four prototypes.
> touch Kano\depends > echo Kano Namespace Namespaces ObjectDescriber > Kano\protos > io -e "Kano println" Exception: Unable to open directory ...\lib\io\addons\Kano\sourceProgress again but why does Io look for C sources? In some addons, I see an empty
source
folder with a .gitisdumb
file in it, to keep the folder in version control. So maybe it is needed.> mkdir Kano\source > io -e "Kano supportedFiles println" list(make.io, Kanofile, kanofile, Kanofile.io, kanofile.io)Nice, no error. Kano works. To be fully useable, there are a few more things to fix:
- Kano is a tool, it comes with
bin/kano
starter script. Each starter script needs a Windows version, i.e. abin/kano.bat
which calls the original starter script,io "%IO_HOME%\lib\io\addons\Kano\bin\kano" %*
And both scripts need to be in thePATH
. The easiest way is to copykano.bat
to%IO_HOME%\bin
, which containsio.exe
. - Kano offers
kano [-T|V|help|ns] [namespace:]taskName arg1 arg2...
as help.-T
lists all tasks defined in the localKanofile
and-V
shows its version. At least it tries to do that. It calls Eerie to get its version. Now Eerie depends on Kano and Kano depends on Eerie, and I do not have Eerie, this is all not good. If you want everything to work, you can replace line 44 in Kano'sNamespaces.io
:V := option( """Prints Kano version.""" - version := Eerie Env named("_base") packageNamed("Kano") \ config at("meta") at("version") + version := File thisSourceFile parentDirectory parentDirectory \ at("package.json") contents parseJson at("version") ("Kano v" .. version) println)
- Much later I noticed that Kano's
package.json
does not have a name field. This is needed for Docio to generate the documentation. Sigh.