操作方法

Io-language 勉強会

cho45 (さとう) / www.lowreal.net

ご注意:Io は発展途上でちょくちょく挙動が変わります。(Io 20080120)

Agenda

Io の立ち位置と雰囲気

Io の機能

だいたい必要なのはある

Io の「メッセージング」

メッセージングは OOP での一番重要なとこ

Io の超基本的な文法 (メソッド呼び出し)

// foobar を表示する Io のコード
"foobar" println
// JS っぽい疑似コード
"foobar".alert();

他の言語だとドットやアローがあるところはスペースになっている

Io の超基本的な文法 (メソッド呼び出し)

list("a", "b", "c") at(0) #=> "a"
list("a", "b", "c") println

引数を渡したいときは括弧が必須

引数をとらないメソッドは括弧を省略可

引数の取扱

# foo に新しい手続きを代入
foo := method("foo called" println)
foo("not shown" println)
# foo callled のみ表示される

重要:引数は基本的には全く評価されない

全部ブロック渡し・関数渡しみたいなもの

引数の取扱 (評価させるには)

# method 定義時に仮引数 arg を書く
foo := method(arg, arg println)
foo("shown" println)
# shown が二回表示される

仮引数 (arg) を書けば自動的にそれは評価される

引数の取扱 (評価させるには)

# 明示的に引数を eval する
foo := method( call evalArgAt(0) println )
foo("shown" println)
# shown が二回表示される

call というスロットは特別重要

関数呼び出し (JS とほぼ同じ)

  1. ローカル変数用のオブジェクトが作られる
    (アクティベーションオブジェクト)
  2. call, self などのスロットを初期化する
    (arguments みたいなのを初期化する)
  3. 本体が実行される

call

制御構文の例を if で

if 1

if (a > 1) then (
	"foo" println
) else (
	"bar" println
)

実はメソッドチェイン

if 1 (JS 等価コード)

if(a > 1).then(function () {
	alert("foo");
}).else(function () {
	alert("bar");
})

if 2

(a > 1) ifTrue( "true" println)

単にメソッドを呼んでるにすぎない

false は ifTrue の引数を評価しない。true ならする、の違いで分岐する

if 2 (Ruby 等価コード)

class TrueClass; def ifTrue(&block) block.call end end
class FalseClass; def ifTrue(&block) end end
true.ifTrue { p "true" }
(a > 1).ifTrue { p "true" }

if 3

if (a > 1, "true", "false")

一番高速、返り値が評価したメッセージの値になる

たぶん一番よく使う。ちょっと読みにくい。

Message オブジェクト

Io の Message とは

こんなことできる

// 普通な感じ
list(1, 2, 3) map(i, i * i) #=> list(1, 4, 9)

// ちょっと特殊 (部分適用のように見える)
list(1, 2, 3) map(*3)       #=> list(3, 6, 9)

map(*3) の原理

List mymap := method(
    m := call message argAt(0);
    map(i, i doMessage(m))
)
list(1, 2, 3) mymap(println)
list(1, 2, 3) mymap(*(3))

対象オブジェクトに引数にあたえられたメッセージを送っている

こんなことできる

# foo := method(mes, "foo #{mes}" interpolate println )

def foo (mes) {
	"foo #{mes}" interpolate println
}
# ↑ もはや Io に見えない。def が function ならまんま JS

Io で def

括弧の省略

引数がある場合は括弧が省略できないと書きましたが、自力で call message をパースすれば省略させられます。

Ioke (Io で Rake っぽいの)

パーサーの挙動を変える

演算子の定義

Io はパーサの挙動を一部変えれます。

演算子の登録

Message fromString(""" "abc" =~ "a." """) code print
#=> "abc" =~ "a."

OperatorTable addOperator("=~", 7)
Message fromString(""" "abc" =~ "a." """) code print
#=> "abc" =~("a.")
# ↑ "a." が =~ の引数になっている

非同期

非同期 (demo)

foo := method(wait(3); 1)
a := foo  #=> 3 秒まって1
a := @foo #=> 即座に Future オブジェクトが返る

a #=> ここで 3 秒まつ
a #=> もう待たないで即座に 1 が返る

非同期指定されたメッセージはキューに入る。

非同期

a := @foo #=> 即座に Future オブジェクトが返る

yield; yield; yield #=> 他のコルーチンに処理をゆずる

a #=> 即座に 1 が返る

非同期

以下若干細かいところ

method_missing

o := Object clone
o forward := method( call message name println )
o foobar #=> foobar が表示される

ブロックとメソッド

foo := method( "foobar" println )
foo #=> 呼び出しされる

bar := block( "foobar" println )
bar call #=> call が必要

あとは self の違い

メソッド

a := Object clone
a foo := method( self )
(a foo == a) println #=> true

b := Object clone
b foo := a getSlot("foo")
(b foo == b) println #=> true

# method の self は呼びだし時に決定するレシーバ

ブロック

b := block(
	(self == Lobby) println #=> true
)
b call
#=> Lobby は JS でいうところの window
(Lobby Lobby == Lobby)

# block の self は外側のローカル変数オブジェクトと同じ

最後に

That's all! Thank you.