SmalltalkからLuaをevalする

  • 6
    いいね
  • 0
    コメント

はじめに

Pharo SmalltalkからTarantool経由でLuaを実行してみようという話です。

Tarantoolをインストール

Tarantoolって何よ? ということですが、DBMSの機能を備えたLuaの実行環境です。ロシア製ということもあって日本ではまだマイナーですが、向こうでは大手のネット企業であるMail.Ru Groupが使ったりもしているようです。

https://tarantool.org/try.html
でいきなりWebブラウザから試せます。

3+4 と打ってenterすると

7 が返ってきますね。

正直ここで試すのは苦痛なのでとっとと本物をインストールします。

macOSであれば

brew install tarantool --HEAD

とします。

その他OS用のインストール方法はこちらです。

https://tarantool.org/download.html

Tarantoolの起動

では動かしてみます。

tarantool とターミナルで打ってみるとプロンプトが出てきます。

tarantool> 3 + 4

7が返ってきました。

tarantool-console.png

tutorial()と打つとインタラクティブなチュートリアルが始まったりしますが、今回は割愛します。

DBMS部分を動かしてみましょう。

box.cfg{listen = 3301}

3301ポートで接続を受け付けるようになりました。

tarantool-listen.png

次に外部接続用のユーザを作成します。'taran'というユーザIDで、パスワードは'talk'にします。

box.schema.user.create('taran', {password = 'talk'})

さらに権限も付与します。データの読み取りだけでなく、スキーマやインデックスの作成、evalもできるようにしておきます。

box.schema.user.grant('taran', 'read,write,execute', 'universe')

これでクライアントが接続できる環境が整いました。

Tarantalkを入れる

TarantalkとはPharo版のTarantoolクライアントです。これによりPharo SmalltalkからTarantoolにつないで、Luaのプログラムを実行したり、DBMSの機能を使ったりすることが可能になります。

TarantoolのクライアントはConnectorと呼ばれています。Smalltalk以外にも、JavaやPython、ErlangやC#などいろいろな言語用のConnnectorが提供されています。

https://tarantool.org/doc/book/connectors/index.html

TarantalkはPharoのCatalog Browserからインストールできます。"Tools"->"Catalog Browser"で開き、検索窓のところに"taran"と打てば見つかるでしょう。

catalog-browser.png

右クリックで"Install stable version"を選ぶと入ります。

install-stable-version.png

もしくはPlaygroundを開き、以下を"Do it"でも良いです。

Metacello new
    smalltalkhubUser: 'Pharo' project: 'MetaRepoForPharo50';
    configuration: 'Tarantalk';
    version: #stable;
    load.

接続の確認

Tarantoolには認証機能があるので、接続の際には、ホスト、ポート番号の他、ユーザIDとパスワードをURIの形で指定します。

tarantalk := TrTarantalk connect: 'taran:talk@localhost:3301'.

"Print it"してみると接続状態を確認できます。

tarantool-connected.png

接続を切るにはreleaseを送ります。

tarantalk release.

tarantool-released.png

TrTarantalk releaseAllで、複数のクライアントをまとめてreleaseすることもできます。色々試した結果、エラーなどでTarantoolへの接続状態がおかしくなった時に使うと便利です。

Luaプログラムの実行

DBMSとしての利用は次回に回すとして、ここではLuaのプログラムをPharoから実行してみることにします。evalWithReturn:を使います。

tarantalk := TrTarantalk connect: 'taran:talk@localhost:3301'.
(tarantalk evalWithReturn: '3+4') value.

#(7)が返ってきました。evalWithReturn:は必ずArrayの形で戻り値が返ります。これはLuaの関数が多値を返すことができるためです。

return7.png

valueと送っているのは、TarantoolのAPIが非同期になっているからです。valueは非同期APIを同期的に使うための便利メッセージです。これにより値が返るのをブロックして待つようになります。

非同期としてそのまま使うにはコールバックをifDone:ifFailedで指定します。

(tarantalk evalWithReturn: '3+4')
    ifDone: [:ret | ret inspect] ifFailed: [:err | err inspect].

"Do it"するとevalの終了時に別のスレッドでインスペクタが上がります。非同期APIはブロックが最小限になるのでスループットが上がるという利点があります。が、記述が面倒なので以後は主にvalueを使います。

inspect7.png

今度は乱数を得てみましょう。

(tarantalk evalWithReturn: 'math.random()') value.

#(0.698852465637164)と返ってきました。
rand.png

もうちょっとコラボ感を出したいので、Luaからの値をもとに、Smalltalk側で現在の時間を生成してみたいと思います。別にDateAndTime nowで良いのですがあえてやります。

(tarantalk evalWithReturn: 'os.time()') valueThen: [ :val |
    DateAndTime fromUnixTime: val first
].

valueThen:valueのバリエーションで、Luaからの値を加工したい時に使います。os.time()の結果が数値の配列として帰るのでfirstで取り出し、DataAntTime class>>fromUnixTime:に渡しているというわけです。

"Inspect it"すると現在時間となっていることを確認できます。Unix epochからの秒数なので、ナノ秒の部分(nanos)は入っていません。

inspect-lua-dateandtime.png

ちなみにDateAndTime nowだとこうなります。

inspect-st-dateandtime.png

Lua側に値を渡す

今度はSmalltalk側からLuaのプログラムに値を渡してみる例です。
値を渡すためにはevalWithReturn:arguments:を使います。Lua側も(...)で任意の引数を受け取れるように書いておく必要があります。

Smalltalkにも正規表現ライブラリはありますが、あえてLuaの文字列パターンマッチを利用してみます。

(tarantalk evalWithReturn: 'string.match(...)' arguments: {'こんにちはSmalltalkの世界へ'. 'S%w+'}) value

'S'で始まるalphanumericな部分が取り出されます。'Smalltalk'ですね。

requireしてみる

TarantoolはLuaの標準ライブラリ以外にも、便利ライブラリを色々と搭載しているので、requireして使ってみたいと思います。

搭載ライブラリについてはこちらに情報があります。
https://tarantool.org/doc/reference/reference_lua/index.html

digestのmoduleを使い、SHA-512のハッシュ値を求めてみたいと思います。Pharoには標準でSHA-256はあるのですが、SHA-512は入っていないのです。ようやくLuaを使う意味が出てきました。:slight_smile:

(tarantalk evalWithReturn: '(require("digest").sha512_hex(...))' arguments: {'こんにちはSmalltalkの世界へ'}) valueThen: [ :val |
    val first
].

それらしい結果が返ってきます。

sha512.png

速さは?

興味本位で速さを測ってみました。1000回ほど繰り返してみます。

[1000 timesRepeat: [  
(tarantalk evalWithReturn: '(require("digest").sha512_hex(...))' arguments: {'こんにちはSmalltalkの世界へ'}) valueThen: [ :val |
    val first
].
]] timeToRun.

perf.png

2013年のMacBook Air (1.7 GHz Intel Core i7)で0:00:00:00.334となりました。1秒間で3000送信くらいはこなせそうです。FFIには遠く及びませんがお気軽には使えるでしょう。

まとめ

Tarantalkというライブラリを使ってSmalltalkからLuaを実行してみようという話でした。豊富なLuaのエコシステムをPharo Smalltalkから利用できるというメリットがあります。
DBMSとしても面白い特徴がありますので、それは次回以降に紹介します。