Io プログラミングガイド


概要

簡易性は幸福の本質である。
- Cedric Bledsoe
Simplicity is the essence of happiness.
- Cedric Bledsoe
Io はプロトタイプベースの動的言語である。 その着想の大半は Smalltalk (全てがオブジェクトである)、Self[2] (プロトタイプベース), NewtonScript[3] (差分継承), Act1 (アクタとFutureによる並行動作)、Lisp[5] (コードは 検査 / 修正 可能なランタイムツリー) そして Lua[6] (小さい、組み込み可能).

観点

なぜ別の言語か?

ここ 30 年のプログラミング言語の研究の焦点はほとんど、Smalltalk のような高水準言語の表現力と、Cのような低級言語の性能を融合することにか注意を払うことばかりに進んできました。 その結果、一連の言語はCほど速くないかSmalltalkほど表現力豊かでないものでした。 Io の目的は、動的プログラミングのよる高いレベルでの探査によって、より大きいレベルでのランタイムの柔軟性、および、単純化されたプログラミングの文法と意味による、表現の豊さに再び焦点を当てることでです。

Io は 全ての値はオブジェクト (継承されたメソッド、スロットを含んでいる場合でも、 何でもランタイムに変更可能)、 全てのコードは式 (それはランタイムに操作、変更可能) であり、 全ての式は動的なメッセージを送信 (代入と制御構造を含む)することで成り立っています。 実行コンテキストは、オブジェクトの集まりとメソッド / ブロックによる活性可能なオブジェクト、そしてブロックによる代入可能なスコープに統一された関数だけです。 並行処理は、アクターとスケーラビリティの為にコルーチンを通して、より簡単に操作しやすく作られています。

目標

言語であるために:

シンプル

パワフル 実用的

はじめましょう

ダウンロード

Io の配布は以下から取得できます:
http://iolanguage.com

インストール

VM

最初に Io vm をコンパイルします:
make vm
sudo make install 

アドオンが依存するライブラリのインストール

いくつかの Io のアドオンは既にあなたのシステム上にインストールされているライブラリを必用とするかもしれません。 それらを自動的にインストールするのは次をタイプしてください:
su -c "sudo make aptget"
または:
su -c "make emerge"
または:
sudo make port
あなたの使用しているパッケージインストーラによって使い分けでください。 (port is for OSX)

アドオンのコンパイル

ビルドをするには、トップディレクトリで以下を実行:
make 
バイナリはサブフォルダ _build/binaries に置かれるでしょう。 インストールするには:
sudo make install 
もしくは、インストールを、単にあなたの開発用フォルダにリンクするようにするには:
sudo make linkInstall
そしてすべてのユニットテストを実行するには:
make test
いくつかのアドオンがビルドできなくても、そのアドオンがあなたが特別必用としていないなら、それは心配しなくても大丈夫です。 アドオンは任意のライブラリです。

ノート

特定のアドオンのみ make するには、以下のようにしてください:
make AddonName
ソースコントロール上のリポジトリから取得してきた後は、必ずしてください:
make clean; make
VM のテストのみ実行:
make testvm

バイナリ

Io は2つの実行可能形式をビルドし、binaries フォルダに配置します。 それらは以下:
io_static
io
io_static は最小量のセットの基本関数が静的にすべてリンクされている vm を含んでいる実行可能な形式です。 io は実行時に、iovm が動的にリンクされたライブラリをロードすることができます。それは、io のアドオンが参照されたときに動的に組み込まれます。

スクリプトの実行

スクリプトを実行する例:
io samples/HelloWorld.io
Io が最初に実行するものに、どんな main() 関数やオブジェクトも必要ありません。 スクリプトはコンパイルされると実行されます。

対話モード

実行:
./_build/binaries/io
もし Io がインストール済みなら、以下のように実行:
io
Io のインタプリタのプロンプトが引かれるでしょう。

直接入力する事で、コードを評価することができます。 例:

Io> "Hello world!" println
==> Hello world!
式は Lobby のコンテキストにて評価される :
Io> print
[lobby のコンテンツが出力される]
あなたのホームフォルダに .iorc が存在する場合、対話モードのプロンプトが開始される前にそれが評価されます。

オブジェクトのインスペクト

このようにしてオブジェクトのスロットのリストを取得することができます:
Io> someObject slotNames
それらをソート順にするには:
Io> someObject slotNames sort
オブジェクトの記述がうまくフォーマットされた形式においては slotSummary メソッドが便利です:
Io> slotSummary
==>  Object_0x20c4e0:
  Lobby            = Object_0x20c4e0
  Protos           = Object_0x20bff0
  exit             = method(...)
  forward          = method(...)
Exploring further:
Io> Protos
==>  Object_0x20bff0:
  Addons           = Object_0x20c6f0
  Core             = Object_0x20c4b0

Io> Protos Addons
==>  Object_0x20c6f0:
  ReadLine         = Object_0x366a10
他の Addons が組み込まれていないので、RadLine だけが Addons に表示されています。

メソッドをインスペクトすると、その時のバージョンで逆コンパイルされたものが表示されるでしょう:

Io> Lobby getSlot("forward")
==> # io/Z_Importer.io:65
method(
    Importer import(call)
)

doFile と doString

対話モードにて doFile メソッドを使用すると、スクリプトを実行することができる:
doFile("scriptName.io")
doFile はレシーバのコンテキストにて評価します。(この場合レシーバは lobby でしょう)。 他のオブジェクトのコンテキストにてスクリプトを評価するには、 doFile メッセージをそれに送信します:
someObject doFile("scriptName.io")
文字列を評価するには doString メソッドを使用することができます:
Io> doString("1+1")
==> 2
また、特定のオブジェクトのコンテキストで文字列を評価するには:
someObject doString("1 + 1")

コマンドライン引数

コマンドライン引数を出力する例:
args foreach(k, v, write("'", v, "'\n"))

launchPath

System の "launchPath" スロットには、実行された最初のソースファイルの位置が格納されている。

構文

より少ないことは、より豊かなこと
- Ludwig Mies van der Rohe
Less is more.
- Ludwig Mies van der Rohe

Io キーワードや文を持たない。 すべてが完全なメッセージで構成された式であり、そのそれぞれがランタイムにアクセス可能なオブジェクトである。 非公式の BNF 記述:
exp        ::= { message | terminator }
message    ::= symbol [arguments]
arguments  ::= "(" [exp [ { "," exp } ]] ")"
symbol     ::= identifier | number | string
terminator ::= "\n" | ";"
For performance reasons, String and Number literal messages have their results cached in their message objects.

メッセージ

メッセージ引数はレシーバに式として渡され評価される。 引数の評価の選択は制御構造を実装するために使用できる 例:
for(i, 1, 10, i println)
a := if(b == 0, c + 1, d)
上記コードでの、"for" と "if" は通常のメッセージであり、特殊形式やキーワードでは無い。

Likewise, dynamic evaluation can be used with enumeration without the need to wrap the expression in a block. 例:

people select(person, person age < 30)
names := people map(person, person name)
Methods like map and select will typically apply the expression directly to the values if only the expression is provided:
people select(age < 30)
names := people map(name)
There is also some syntax sugar for operators (including assignment), which are handled by an Io macro executed on the expression after it is compiled into a message tree. Some sample source code:
Account := Object clone
Account balance := 0
Account deposit := method(amount,
    balance = balance + amount
)

account := Account clone
account deposit(10.00)
account balance println
Like Self[2], Io's syntax does not distinguish between accessing a slot containing a method from one containing a variable.

演算子

演算子とは、単に英数字以外の文字による名前をもつメソッドである。例外として、":"(コロン), "_"(アンダースコア), '"'(ダブルクォート), "."(ドット) は演算子に使えず、また次のものは英数字による名前だが演算子とする: "and", "or", "return". 例:
1 + 2
すなわち次のような通常の形式のメッセージにコンパイルされる:
1 +(2)
括弧を使ってまとめることもできる:
1 +(2 * 4)
標準の演算子は、C 言語と同じ優先順位で処理される。従って:
1 + 2 * 3 + 4
は次のように解析される:
1 +(2 *(3)) +(4)
ユーザ定義の演算子 (標準の演算子名は使えない) は、常に左から右に動作する。

代入

Io は3つの代入演算子を持つ:

operator action
::= スロットの生成, setter の生成, 値の代入
:= スロットの生成, 値の代入
= スロットが存在すれば値の代入、もしなければ例外を発生する

These operators are compiled to normal messages whose methods can be overridden. 例:

ソースコード コンパイル結果
a ::= 1 newSlot("a", 1)
a := 1 setSlot("a", 1)
a = 1 updateSlot("a", 1)

On Locals objects, updateSlot is overridden so it will update the slot in the object in which the method was activated if the slot is not found the locals. This is done so update assignments in methods don't require self to be an explicit target.

数値

以下は、有効な数値フォーマットの例です:
123
123.456
0.456
.456
123e-4
123e4
123.456e-7
123.456e2
16進数もサポートされます(どんなケースでも):
0x0
0x0F
0XeE

文字列

Strings can be defined surrounded by a single set of double quotes with escaped quotes (and other escape characters) within.
s := "this is a \"test\".\nThis is only a test."
Or for strings with non-escaped characters and/or spanning many lines, triple quotes can be used.
s := """this is a "test".
This is only a test."""

コメント

コメントには //, /**/ と # のスタイルをサポートしています。 例:
a := b // add a comment to a line

/* comment out a group
a := 1
b := 2
*/
"#" スタイルは Unix でのスクリプトに役立ちます:
#!/usr/local/bin/io
これだけだ! 貴方は Io の文法について知るために必要なこと全てを知っている。 制御構造・オブジェクト・メソッド・例外は 上記で説明した構文で表現されます。

オブジェクト

今まで考案された全ての言語 - Fortran, Algol60, Lisp, APL, Cobol, Pascal - では、プログラムは一方に受動的なデータオブジェクトを持ち、もう一方にそれを操作する実行可能なプログラムを持つ構造だった。 この二部構成を同質に塗り替え、それぞれが自身で操作できるデータ系の集合で構成したのが、オブジェクト指向プログラムである。
- David Gelernter and Suresh J Jag
In all other languages we've considered [Fortran, Algol60, Lisp, APL, Cobol, Pascal], a program consists of passive data-objects on the one hand and the executable program that manipulates these passive objects on the other. Object-oriented programs replace this bipartite structure with a homogeneous one: they consist of a set of data systems, each of which is capable of operating on itself.
- David Gelernter and Suresh J Jag

概略

Io's guiding design principle is simplicity and power through conceptual unification.

concept unifies
blocks with assignable scope functions, methods, closures, functions
prototypes objects, classes, namespaces, locals
messages operators, calls, assignment, variable accesses

プロトタイプ

Io では、(ローカルのブロック記憶領域や名前空間それ自体を含む) 全てがオブジェクトであり、(値の代入を含む) 全ての振舞いはメッセージです。 。オブジェクトはスロットというキーと値のペアのリストと、protos という継承したオブジェクトの内部リストから出来ています。 あるスロットのキーはシンボル(つまり、ユニークな不変の文字列)であり、あらゆる型のオブジェクトをその値に取ります。 新たなオブジェクトの clone と init で、既にあるオブジェクトのクローンを作成します。 あるクローンは、protos のリストにその親を持つ空のオブジェクトです。 新しいインスタンスの init スロットは有効になり、そのオブジェクトを初期化するために使われます。 NewtonScript のように、Io のスロットは create-on-write です。
me := Person clone
インスタンスに変数やメソッドを追加するには、単純にセットします:
myDog name := "rover"
myDog sit := method("I'm sitting\n" print)
あるオブジェクトのクローンが作成されると、その init スロットがあれば、それが呼ばれます。

継承

あるオブジェクトが該当するスロットを探すようなメッセージを受けた場合、見つからなければ、proto リストを深さ優先で再帰的に検査を続けます。 検査のループが見つかった場合には、検査は中断されます。 マッチするスロットに実行可能なオブジェクトがあれば、例えば Block や CFunction などの場合、それが実行され、値があればその値を返します。 Io にはグローバルオブジェクトはなく、Lobby という Io 名前空間のルートオブジェクトがあります。

クラスがないため、サブクラスとインスタンスの違いはありません。 以下は、サブクラスに相当するものを作成する例です:

Io> Dog := Object clone
==> Object_0x4a7c0 
上記のコードでは、Lobby の "Dog" スロットに Object オブジェクトのクローンをセットしています。 そのクローンは Object への参照を含む protos リストのみを持っています。 これで、本質的に Dog は Object のサブクラスとなります。 インスタンスの変数やメソッドはその proto から継承されます。 スロットがセットされた場合、proto は変更されず、作成したオブジェクトに新しいスロットを作成します:
  Io> Dog color := "red"
  Io> Dog
  ==> Object_0x4a7c0:
    color := "red"

多重継承

あるオブジェクトのprotosリストには、protoを幾つでも追加できます。 メッセージへの応答の際、検査構造は proto チェーンを深さ優先で検査します。

メソッド

メソッドは、呼び出された際、生成された際のオブジェクトをローカルに保存し、そのローカルの proto ポインタとそれ自身のスロットをターゲットにメッセージをセットする匿名の関数です。 Object メソッド method() でメソッドを作成することができます。 例:
method((2 + 2) print)
オブジェクト内のメソッドを使用する例:
Dog := Object clone
Dog bark := method("woof!" print)
上記コードは、Dog と名の付けられたオブジェクトの "サブクラス" を新たに作成し、"woof!" を出力するブロックである bark スロットを含んでいます。 メソッドを呼び出す例:
Dog bark
ブロックのデフォルトの戻り値は、最後の式の結果になります。

引数

メソッドは引数をとるようにも定義できる。 例:
add := method(a, b, a + b)
一般的な形式:

method(<arg name 0>, <arg name 1>, ..., <do message>)

ブロック

ブロックは変数のスコープの違いを除いて、メソッドと同じです。 つまり変数検査は、ブロックが実行されるメッセージのターゲットの代わりに、ブロックが生成された時点での文脈に従います。 ブロックは Object の block() メソッドで生成できます。 ブロックを生成する例:
b := block(a, a + b)

ブロック vs メソッド

混乱の種になりそうなので、詳細を説明します。 両方とも、呼び出されるとローカル領域にオブジェクトを作成するのは同じです。 違いは、ローカル領域のオブジェクト "proto" と "self" に何がセットされるかです。 メソッドでは、それらのスロットには、メッセージのターゲットがセットされる。 ブロックでは、そのブロックが作り出されたときのローカル領域オブジェクトがセットされる。 従ってブロック中では、ローカル領域にない変数は、そのブロックが作成されたときのローカル領域を辿って検索される。 そしてメソッド中では、ローカル領域にない変数は、そのメソッドを呼び出したオブジェクトを辿って検索される。

call と self スロット

ローカル領域にオブジェクトが作られる際、その self スロットには、メソッドの場合はメッセージのターゲット、ブロックの場合は生成時の文脈に沿ってセットされ、call スロットには Call オブジェクトがセットされ、ブロック実行時のアクセス情報が参照できます:

slot returns
call sender 呼び出し側のローカルオブジェクト
call message メソッドまたは、ブロックへの呼び出し時に使われたメッセージ
call activated 実行されたメソッドまたはブロック
call slotContext スロットが見つかった文脈
call target カレントオブジェクト

可変長引数

The "call message" slot in locals can be used to access the unevaluated argument messages. Example of implementing if() within Io:
myif := method(
    (call sender doMessage(call message argAt(0))) ifTrue( 
    call sender doMessage(call message argAt(1))) ifFalse( 
    call sender doMessage(call message argAt(2)))
)

myif(foo == bar, write("true\n"), write("false\n"))
The doMessage() method evaluates the argument in the context of the receiver. A shorter way to express this is to use the evalArgAt() method on the call object:
myif := method(
    call evalArgAt(0) ifTrue(
    call evalArgAt(1)) ifFalse( 
    call evalArgAt(2))
)

myif(foo == bar, write("true\n"), write("false\n"))

転送

If an object doesn't respond to a message, it will invoke its "forward" method if it has one. Here's an example of how to print the information related lookup that failed:
MyObject forward := method(
    write("sender = ", call sender, "\n")
    write("message name = ", call message name, "\n")
    args := call message argsEvaluatedIn(call sender)
    args foreach(i, v, write("arg", i, " = ", v, "\n") )
)

再送

Sends the current message to the receiver's proto with the context of self. 例:
A := Object clone
A m := method(write("in A\n"))
B := A clone
B m := method(write("in B\n"); resend)
B m
will print:
in B
in A
For sending other messages to the receiver's proto, super is used.

スーパー

Sometimes it's necessary to send a message directly to a proto. 例:
Dog := Object clone
Dog bark := method(writeln("woof!"))

fido := Dog clone
fido bark := method(
    writeln("ruf!")
    super(bark)
)
Both resend and super are implemented in Io.

イントロスペクション

Using the following methods you can introspect the entire Io namespace. There are also methods for modifying any and all of these attributes at runtime.

slotNames

The slotNames method returns a list of the names of an object's slots:
Io> Dog slotNames
==> list("bark")

protos

The protos method returns a list of the objects which an object inherits from:
Io> Dog protos
==> list("Object")

getSlot

The "getSlot" method can be used to get the value of a block in a slot without activating it:
myMethod := Dog getSlot("bark")
Above, we've set the locals object's "myMethod" slot to the bark method. It's important to remember that if you then want use the myMethod without activating it, you'll need to use the getSlot method:
otherObject newMethod := getSlot("myMethod")
Here, the target of the getSlot method is the locals object.

code

The arguments and expressions of methods are open to introspection. A useful convenience method is "code", which returns a string representation of the source code of the method in a normalized form.
Io> method(a, a * 2) code
==> "method(a, a *(2))"

コントロール・フロー

true, false と nil

Io has predefined singletons for true, false and nil. true and false are used for boolean truth values and nil is typically used to indicate an unset or missing or unavailable value.

比較

The standard comparison operations (==, !=, >=, <=, >, <) return either the true or false.
Io> 1 < 2
==> true
The compare() method is used to implement the compare methods and returns -1, 0 or 1.

条件分岐

if

The Lobby contains the condition and loop methods. A condition looks like:

if(<condition>, <do message>, <else do message>)
例:
if(a == 10, "a is 10" print)
The else argument is optional. The condition is considered false if the condition expression evaluates to false or nil, and is considered true otherwise.

The result of the evaluated message is returned, so:

if(y < 10, x := y, x := 0)
is the same as:
x := if(y < 10, y, 0)
Conditions can also be used in this form (though not as efficiently):
if(y < 10) then(x := y) else(x := 2)
Else-if is supported:
if(y < 10) then(x := y) elseif(y == 11) then(x := 0) else(x := 2)
As well as Smalltalk style ifTrue, ifFalse, ifNil and ifNonNil methods:
(y < 10) ifTrue(x := y) ifFalse(x := 2)
Notice that the condition expression must have parenthesis surrounding it.

ループ

loop

loop メソッドは "無限" ループに使われます:
loop("foo" println)

while

条件分岐のように、ループは単なるメッセージです。 while() は次の引数を必要とします:

while(<condition>, <do message>)
例:
a := 1
while(a < 10, 
    a print
    a = a + 1
)

for

for() は次の引数を必要とします:

for(<counter>, <start>, <end>, <optional step>, <do message>)
start と end のメッセージは、ループの開始時に一度だけ評価されます。 例:
for(a, 0, 10, 
    a println
)
ステップによる例:
for(x, 0, 10, 3, x println)
次の出力になります:
0
3
6
9
ループを逆順に実行するには、負の値をステップに加えてください:
for(a, 10, 0, -1, a println)
Note: the first value will be the first value of the loop variable and the last will be the last value on the final pass through the loop. So a loop of 1 to 10 will loop 10 times and a loop of 0 to 10 will loop 11 times. Example of using a block in a loop:
test := method(v, v print)
for(i, 1, 10, test(i))

repeat

The Number repeat method is simpler and more efficient when a counter isn't needed.
3 repeat("foo" print)
==> foofoofoo

break と continue

フロー制御オペレーションの break と continue は ループ中でサポートされます。 例:
for(i, 1, 10, 
    if(i == 3, continue)
    if(i == 7, break)
    i print
)
出力結果:
12456

return

ブロックのどの部分でも return メソッドを使用して即座に返ることができます。 例:
Io> test := method(123 print; return "abc"; 456 print)
Io> test
123
==> abc

インポート

The Importer proto implements Io's built-in auto importer feature. If you put each of your proto's in their own file, and give the file the same name with and ".io" extension, the Importer will automatically import that file when the proto is first referenced. The Importer's default search path is the current working directory, but can add search paths using its addSearchPath() method.

並行動作

コルーチン

Io uses coroutines (user level cooperative threads), instead of preemptive OS level threads to implement concurrency. This avoids the substantial costs (memory, system calls, locking, caching issues, etc) associated with native threads and allows Io to support a very high level of concurrency with thousands of active threads.

スケジューラ

The Scheduler object is responsible for resuming coroutines that are yielding. The current scheduling system uses a simple first-in-first-out policy with no priorities.

アクタ

An actor is an object with its own thread (in our case, its own coroutine) which it uses to process its queue of asynchronous messages. Any object in Io can be sent an asynchronous message by placing a @ or @@ before the message name. (think of the "a" in @ as standing for "asynchronous")

例:

result := self foo // synchronous 
futureResult := self @foo // async, immediately returns a Future
self @@foo // async, immediately returns nil
When an object receives an asynchronous message it puts the message in its queue and, if it doesn't already have one, starts a coroutine to process the messages in its queue. Queued messages are processed sequentially in a first-in-first-out order. Control can be yielded to other coroutines by calling "yield". 例:
obj1 := Object clone
obj1 test := method(for(n, 1, 3, n print; yield))
obj2 := obj1 clone
obj1 @@test; obj2 @@test
while(Scheduler yieldingCoroutines size > 1, yield)
これは "112233" と出力される. Here's a more real world example:
HttpServer handleRequest := method(aSocket,
    HttpRequestHandler clone @@handleRequest(aSocket)
)

Future

Io's futures are transparent. That is, when the result is ready, they become the result. If a message is sent to a future (besides the two methods it implements), it waits until it turns into the result before processing the message. Transparent futures are powerful because they allow programs minimize blocking while also freeing the programmer from managing the fine details of synchronization.

自動デッドロック検知

An advantage of using futures is that when a future requires a wait, it will check to see if pausing to wait for the result would cause a deadlock and if so, avoid the deadlock and raise an exception. It performs this check by traversing the list of connected futures.

@ 演算子と @@ 演算子

The @ or @@ before an asynchronous message is just a normal operator message. So:
self @test
Gets parsed as(and can be written as):
self @(test)

Futures and the Command Line Interface

The command line will attempt to print the result of expressions evaluated in it, so if the result is a Future, it will attempt to print it and this will wait on the result of Future. 例:
Io> q := method(wait(1))
Io> @q
[1-second delay]
==> nil
To avoid this, just make sure the Future isn't the result. 例:
Io> @q; nil
[no delay]
==> nil

Yield

An object will automatically yield between processing each of its asynchronous messages. The yield method only needs to be called if a yield is required during an asynchronous message execution.

一時停止と再開

It's also possible to pause and resume an object. See the concurrency methods of the Object primitive for details and related methods.

例外

例外の発生

An exception can be raised by calling raise() on an exception proto.
Exception raise("generic foo exception")

Try と Catch

To catch an exception, the try() method of the Object proto is used. try() will catch any exceptions that occur within it and return the caught exception or nil if no exception is caught.

e := try(<doMessage>)
To catch a particular exception, the Exception catch() method can be used. 例:
e := try(
    // ...
) 

e catch(Exception,
    writeln(e coroutine backtraceString)
)
The first argument to catch indicates which types of exceptions will be caught. catch() returns the exception if it doesn't match and nil if it does.

Pass

To re-raise an exception caught by try(), use the pass method. This is useful to pass the exception up to the next outer exception handler, usually after all catches failed to match the type of the current exception:
e := try(
    // ...
) 

e catch(Error,
    // ...
) catch(Exception,
    // ...
) pass

カスタム例外

Custom exception types can be implemented by simply cloning an existing Exception type:
MyErrorType := Error clone

プリミティブ

プリミティブは Io の組込みオブジェクトで、主に C で実装され、幾つか隠蔽データを持っています。 例えば、Number プリミティブは倍制度浮動小数点数のデータを隠蔽し、計算操作のメソッドは C 関数で実装されています。 全ての Io プリミティブは Object プロトタイプを継承し、可変です。 つまりそのメソッドは変更可能です。 より詳しい情報は、リファレンスドキュメントを参照してください。

このドキュメントはリファレンスマニュアルではなく、基底プリミティブについての概説であり、さくっと触り、何が出来るかを書いています。リファレンスドキュメントのどこを見れば詳しく書いてあるかもわかるでしょう。

オブジェクト

? 演算子

Sometimes it's desirable to conditionally call a method only if it exists (to avoid raising an exception). 例:
if(obj getSlot("foo"), obj foo)
Putting a "?" before a message has the same effect:
obj ?foo

リスト

リストは配列の参照であり、すべての標準的な配列操作と列挙のメソッドをサポートします。 例:

空のリストを生成:

a := List clone
list() メソッドを使用することで、任意のリストを生成することができます:
a := list(33, "a")
アイテムの追加:
a append("b")
==> list(33, "a", "b")
リストのサイズを取得:
a size
==> 3
インデックスを指定し、アイテムの取得(リストのインデックスはゼロから開始されます):
a at(1)
==> "a"
注: リストのインデックスはゼロから開始されます。また、アクセスしたインデックスが存在しない場合は nil を返します。

インデックスを指定し、アイテムのセット:

a atPut(2, "foo")
==> list(33, "a", "foo", "b")

a atPut(6, "Fred")
==> Exception: index out of bounds
インデックスを指定し、アイテムの削除:
a remove("foo")
==> list(33, "a", "b")
インデックスを指定し、アイテムの挿入:
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.

Sequence

Io では、Symbol と呼ぶ不変 Sequence と、Buffer や String と同等な可変 Sequence があります。 文字列(ソースコード内で""で囲まれたものが一例)は Symbol です。 変更操作は Symbol では実行されないので、asMutable メソッドで Symbol の可変なコピーを作成し、そのコピーに対して変更操作を実行します。 共通して、文字列の長さを取得する操作があります:
"abc" size
==> 3
部分一致をチェック:
"apples" containsSeq("ppl")
==> true
位置Nの文字 (byte) を取得:
"Kavi" at(1)
==> 97
スライス:
"Kirikuro" slice(0, 2)
==> "Ki"

"Kirikuro" slice(-2)  # NOT: slice(-2, 0)!
==> "ro"

Io> "Kirikuro" slice(0, -2)
# "Kiriku"
ホワイトスペース除去:
"  abc  " adMutable strip
==> "abc"

"  abc  " asMutable lstrip
==> "abc  "

"  abc  " asMutable rstrip
==> "  abc"
大文字 / 小文字変換:
"Kavi" asUppercase
==> "KAVI"
"Kavi" asLowercase
==> "kavi"
文字列の分割:
"the quick brown fox" split
==> list("the", "quick", "brown", "fox")
同様に、他の文字で分割も可能
"a few good men" split("e")
==> list("a f", "w good m", "n")
数値へ変換:
"13" asNumber
==> 13

"a13" asNumber
==> nil
文字列の補間:
name := "Fred"
==> Fred
"My name is #{name}" interpolate
==> My name is Fred
 
Interpolate は #{} で囲まれた全てを、Io のローカルの文脈コードとして評価します。 コードには制御構文も記述できますが、 asString に対応するオブジェクトを返却する必要があります。

Ranges

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. 例:
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.

ファイル

The 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. 例:
f := File with("foo.txt)
f remove
f openForUpdating
f write("hello world!")
f close

ディレクトリ

ディレクトリ(directory) オブジェクトの生成:
dir := Directory with("/Users/steve/")
ディレクトリ内の全てのファイルのファイルオブジェクトのリストを取得:
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 existence:
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

ネットワーク

All of Io's networking is done with asynchronous sockets underneath, but operations like reading and writing to a socket appear to be synchronous since the calling coroutine is unscheduled until the socket has completed the operation, or a timeout occurs. Note that you'll need to first reference the associated addon in order to cause it to load before using its objects. In these examples, you'll have to reference "Socket" to get the Socket addon to load first.

URL オブジェクトの生成:

url := URL with("http://example.com/")
Fetching an URL:
data := url fetch  
Streaming a URL to a file:
url streamTo(File with("out.txt"))
A simple whois client:
whois := method(host,
    socket := Socket clone setHostName("rs.internic.net") setPort(43) 
    socket connect streamWrite(host, "\n")
    while(socket streamReadNextChunk, nil)
    return socket readBuffer
)
A minimal web server:
WebRequest := Object clone do(
    handleSocket := method(aSocket,
        aSocket streamReadNextChunk
        request := aSocket readBuffer betweenSeq("GET ", " HTTP")
        f := File with(request) 
        if(f exists, f streamTo(aSocket), aSocket streamWrite("not found"))
        aSocket close
    )
)

WebServer := Server clone do(
    setPort(8000)
    handleSocket := method(aSocket, 
        WebRequest clone @handleSocket(aSocket)
    )
)

WebServer start

XML

Using the XML parser to find the links in a web page:
SGML // reference this to load the SGML addon
xml := URL with("http://www.yahoo.com/") fetch asXML
links := xml elementsWithName("a") map(attributes at("href"))

Vector

Io's Vectors are built on its Sequence primitive and are defined as:
Vector := Sequence clone setItemType("float32")
The Sequence primitive supports SIMD acceleration on a number of float32 operations. Currently these include add, subtract, multiple and divide but in the future can be extended to support most math, logic and string manipulation related operations. Here's a small example:
iters := 1000
size := 1024
ops := iters * size

v1 := Vector clone setSize(size) rangeFill
v2 := Vector clone setSize(size) rangeFill

dt := Date secondsToRun(
    iters repeat(v1 *= v2)
)

writeln((ops/(dt*1000000000)) asString(1, 3), " GFLOPS")
Which when run on 2Ghz Mac Laptop, outputs:
1.255 GFLOPS
A similar bit of C code (without SIMD acceleration) outputs:
0.479 GFLOPS
So for this example, Io is about three times faster than plain C.

Unicode

Sequences

In Io, symbols, strings, and vectors are unified into a single Sequence prototype which is an array of any available hardware data type such as:
uint8, uint16, uint32, uint64
int8, int16, int32, int64
float32, float64

サポートしているエンコード

Also, a Sequence has a encoding attribute, which can be:
number, ascii, ucs2, ucs4, utf8
UCS-2 and UCS-4 are the fixed character width versions of UTF-16 and UTF-32, respectively. A String is just a Sequence with a text encoding, a Symbol is an immutable String and a Vector is a Sequence with a number encoding.

UTF encodings are assumed to be big endian.

Except for input and output, all strings should be kept in a fixed character width encoding. This design allows for a simpler implementation, code sharing between vector and string ops, fast index-based access, and SIMD acceleration of Sequence operations. All Sequence methods will do automatic type conversions as needed.

Source

Io source files are assumed to be in UTF8 (of which ASCII is a subset). When a source file is read, it s symbols and strings are stored in Sequences in their minimal fixed character width encoding. 例:
Io> "hello" encoding
==> ascii

Io> "π" encoding
==> ucs2

Io> "∞" encoding
==> ucs2
We can also inspect the internal representation:
Io> "π" itemType
==> uint16

Io> "π" itemSize
==> 2

変換

Sequence オブジェクトにはいくつかの変換メソッドがあります:
asUTF8
asUCS2
asUCS4

組み込み

Conventions

Io's C code is written using object oriented style conventions where structures are treated as objects and functions as methods. Familiarity with these may help make the embedding APIs easier to understand.

Structures

Member names are words that begin with a lower case character with successive words each having their first character upper cased. Acronyms are capitalized. Structure names are words with their first character capitalized. 例:
typdef struct 
{
    char *firstName;
    char *lastName;
    char *address;
} Person;

Functions

Function names begin with the name of structure they operate on followed by an underscore and the method name. Each structure has a new and free function.

例:

List *List_new(void);
void List_free(List *self);
All methods (except new) have the structure (the "object") as the first argument the variable is named "self". Method names are in keyword format. That is, for each argument, the method name has a description followed by an underscore. The casing of the descriptions follow that of structure member names.

例:

int List_count(List *self); /* no argument */
void List_add_(List *self, void *item); /* one argument */
void Dictionary_key_value_(Dictionary *self, char *key, char *value); 

File Names

Each structure has its own separate .h and .c files. The names of the files are the same as the name of the structure. These files contain all the functions(methods) that operate on the given structure.

IoState

An IoState can be thought of as an instance of an Io "virtual machine", although "virtual machine" is a less appropriate term because it implies a particular type of implementation.

Multiple states

Io is multi-state, meaning that it is designed to support multiple state instances within the same process. These instances are isolated and share no memory so they can be safely accessed simultaneously by different os threads, though a given state should only be accessed by one os thread at a time.

Creating a state

Here's a simple example of creating a state, evaluating a string in it, and freeing the state:
#include "IoState.h"

int main(int argc, const char *argv[])
{
    IoState *self = IoState_new();
    IoState_init(self);
    IoState_doCString_(self, "writeln(\"hello world!\"");
    IoState_free(self);
    return 0;
}

Values

We can also get return values and look at their types and print them:
IoObject *v = IoState_doCString_(self, someString);
char *name = IoObject_name(v);
printf("return type is a ‘%s', name);
IoObject_print(v);

Checking value types

There are some macro short cuts to help with quick type checks:
if (ISNUMBER(v))
{
    printf("result is the number %f", IoNumber_asFloat(v));
} 
else if(ISSEQ(v))
{
    printf("result is the string %s", IoSeq_asCString(v));
}
else if(ISLIST(v))
{
    printf("result is a list with %i elements", IoList_rawSize(v));
}
Note that return values are always proper Io objects (as all values are objects in Io). You can find the C level methods (functions like IoList_rawSize()) for these objects in the header files in the folder Io/libs/iovm/source.

バインディング

Documentation on how to write bindings/addons forthcoming..

付録

文法

messages

expression ::= { message | sctpad }
message ::= [wcpad] symbol [scpad] [arguments]
arguments ::= Open [argument [ { Comma argument } ]] Close
argument ::= [wcpad] expression [wcpad]
symbols
symbol ::= Identifier | number | Operator | quote
Identifier ::= { letter | digit | "_" }
Operator ::= { ":" | "." | "'" | "~" | "!" | "@" | "$" | 
"%" | "^" | "&" | "*" | "-" | "+" | "/" | "=" | "{" | "}" | 
"[" | "]" | "|" | "\" | "<" | ">" | "?" }

quotes

quote ::= MonoQuote | TriQuote
MonoQuote ::= """ [ "\"" | not(""")] """
TriQuote ::= """"" [ not(""""")] """""

spans

Terminator ::= { [separator] ";" | "\n" | "\r" [separator] }
separator ::= { " " | "\f" | "\t" | "\v" }
whitespace ::= { " " | "\f" | "\r" | "\t" | "\v" | "\n" }
sctpad ::= { separator | Comment | Terminator }
scpad ::= { separator | Comment }
wcpad ::= { whitespace | Comment }

comments

Comment ::= slashStarComment | slashSlashComment | poundComment
slashStarComment ::= "/*" [not("*/")] "*/"
slashSlashComment ::= "//" [not("\n")] "\n"
poundComment ::= "#" [not("\n")] "\n"

numbers

number ::= HexNumber | Decimal
HexNumber ::= "0" anyCase("x") { [ digit | hexLetter ] }
hexLetter ::= "a" | "b" | "c" | "d" | "e" | "f"
Decimal ::= digits | "." digits | digits "." digits ["e" [-] digits]

characters

Comma ::= ","
Open ::= "(" | "[" | "{"
Close ::= ")" | "]" | "}"
letter ::= "a" ... "z" | "A" ... "Z"
digit ::= "0" ... "9"
digits ::= { digit }

協力者

Io is the product of all the talented folks who taken the time and interest to make a contribution. The complete list of contributors is difficult to keep track of, but some of the recent major contributors include; Jonathan Wright, Jeremy Tregunna, Mike Austin, Chris Double, Rich Collins, Oliver Ansaldi, James Burgess, Baptist Heyman, Ken Kerahone, Christian Thater, Brian Mitchell, Zachary Bir and many more. The mailing list archives, repo inventory and release history are probably the best sources for a more complete record of individual contributions.

参考

1 Goldberg, A et al.
Smalltalk-80: The Language and Its Implementation
Addison-Wesley, 1983
2 Ungar, D and Smith,
RB. Self: The Power of Simplicity
OOPSLA, 1987
3 Smith, W.
Class-based NewtonScript Programming
PIE Developers magazine, Jan 1994
4 Lieberman
H. Concurrent Object-Oriented Programming in Act 1
MIT AI Lab, 1987
5 McCarthy, J et al.
LISP I programmer's manual
MIT Press, 1960
6 Ierusalimschy, R, et al.
Lua: an extensible extension language
John Wiley & Sons, 1996

ライセンス

Copyright 2006, 2007, 2008 Steve Dekorte. All rights reserved.

Redistribution and use of this document with or without modification, are permitted provided that the copies reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

This documentation is provided "as is" and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the authors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this documentation, even if advised of the possibility of such damage.