コルーチン#
コルーチンは、関数の中断(サスペンド)と再開、非同期処理の派生(コルーチンの起動)からなるプログラミングの概念です。
コルーチンの起動#
LAUNCH関数を使用して新たなコルーチンを起動できます。
LAUNCH: 新しいコルーチンを起動する#
<T> LAUNCH(function: () -> T): PROMISE<T>
functionをコルーチンとして非同期に起動します。
起動されたコルーチンは、LAUNCHの呼び出し元のスレッドが次にサスペンドした際に実行されます。
この関数はfunctionの戻り値もしくはfunction内でスローされた値が格納されるPROMISEを返します。
$ xa '
promise := LAUNCH ( =>
"apple"
)
promise::await()
'
# apple
functionは呼び出し元とは独立して起動され、呼び出し元スレッドがサスペンドされ次第実行されます。
$ xa '
result := PROMISE.new()
LAUNCH ( =>
result::complete("apple")
)
result::await()
'
# apple
function内で何らかの値がスローされた場合、その値は標準エラー出力にも出力されます。
$ xa -q 'LAUNCH ( => !!"Error!" )' > /dev/null
# COROUTINE[1]: Error!
functionの戻り値がストリームだった場合、自動的に1度だけイテレートし、その要素列のコピーを保持し、await時にはコピーされたストリームが返されます。
このため、LAUNCHブロックの末尾の処理はどのような状況でも必ず丁度1回だけ実行されます。
$ xa '
promises := [PROMISE.new(), PROMISE.new(), PROMISE.new()]
LAUNCH ( =>
0 .. 2 | (
promises(_)::complete(_ * 10)
)
)
promises(0)::await(), promises(1)::await(), promises(2)::await()
'
# 0
# 10
# 20
$ xa '
counter := 0
promise := LAUNCH ( =>
1 .. 3 | (
counter = counter + 1
counter
)
)
[promise::await()], [promise::await()], [promise::await()], counter
'
# [1;2;3]
# [1;2;3]
# [1;2;3]
# 3
以下はLAUNCHを使って非同期的に標準入力からの命令を受け付けるサンプルです。
先頭のブロック部分を削除すれば、実際にユーザーからのstop命令でプログラムが終了します。
$ { sleep 0.5; echo stop; } | xa -q '
stop := PROMISE.new()
LAUNCH ( =>
IN | (
_ == "stop" && (
OUT << "Stopping..."
stop::complete()
break!!
)
) !: break
)
LOOP | i, _ => (
stop::isCompleted() && break!!
SLEEP << 100
) !: break
OUT << "Stopped!"
'
# Stopping...
# Stopped!
LAUNCH2: 新しいコルーチンを起動する#
<T> LAUNCH2(function(): T): PROMISE<T>
functionをコルーチンとして非同期に起動します。
LAUNCHと同等ですが、引数を式渡し引数として受け取ります。
$ xa '
promise := LAUNCH2 ((
"apple"
))
promise::await()
'
# apple
PROMISE: 非同期結果コンテナ#
PROMISEは、遅延して内容が確定するコンテナです。
new: 新しいPROMISEを生成する#
<T> PROMISE.new(): PROMISE<T>
未完了の新しいPROMISEを生成します。
complete: PROMISEを完了する#
<T> PROMISE<T>::complete([value: T]): NULL
PROMISEをVALUEの内容で完了します。
valueが省略された場合、NULLを内容としてPROMISEを完了します。
fail: PROMISEを失敗として完了する#
<T> PROMISE<T>::fail([error: VALUE]): NULL
PROMISEをerrorで失敗として完了します。
errorがERROR型の値である場合は、その値そのものではなく、それが表しているネイティブエラーが失敗の原因になります。
await: PROMISEの完了を待機し、内容を取得する#
<T> PROMISE<T>::await(): T
PROMISEの内容が完了するまで待機し、その内容を返します。
PROMISEが失敗として完了した場合、awaitはその例外値をスローします。
$ xa '
promise := PROMISE.new()
promise::fail("ERROR!!")
promise::await() !? ( e => e )
'
# ERROR!!
awaitException: PROMISEの完了を待機し、例外値を取得する#
<T> PROMISE<T>::awaitException(): VALUE
PROMISEの内容が完了するまで待機します。
PROMISEが失敗として完了した場合、その例外値を返します。
失敗の原因がネイティブエラーである場合、その例外値はERROR型の値になります。
PROMISEが正常に完了した場合、NULLを返します。
$ xa '
promise := PROMISE.new()
promise::fail("ERROR!!")
promise::awaitException()
'
# ERROR!!
$ xa '
promise := PROMISE.new()
promise::complete("OK")
promise::awaitException()
'
# NULL
isCompleted: PROMISEの完了状態を調べる#
<T> PROMISE<T>::isCompleted(): BOOLEAN
PROMISEが完了、もしくは失敗として完了しているかどうかを返します。
SLEEP: 指定時間の間処理を停止#
SLEEP([milliseconds: NUMBER]): NULL
millisecondsだけ処理を停止します。
APIバージョン5からは、引数はミリ秒ではなく秒として解釈されるようになり、小数による指定も可能になります。
この関数はスレッドをブロッキングせず、関数をサスペンドします。
millisecondsが0もしくは省略された場合、関数を一度サスペンドし、即復帰します。
以下のサンプルコードでは、実行後1秒おいてからHello, World!が出力されます。
$ xa '
SLEEP(1000)
"Hello, World!"
'
# Hello, World!