はじめに
Crystal言語では eval
を使うことができません。
もしもCrystal言語で eval
を使わなければならないとしたら、そのためにはCrystalそのものを実行ファイルの中に組み込まなければならないからです。
Evalはバッドプラクティスとされることが多く、なるべく利用しない方がよいと言われています。しかし、Crystal言語を使っていると、Rubyのevalが恋しくなることがありますね。そんなときに、なんとmruby(やCRuby)をCrystalの実行ファイルに組み込むことによって、Crystal言語でもevalを実行できるようにしてしまう1、というスゴいプロジェクトを見つけたので紹介します。
Anyolite
アニョライトって知っていますか? 私は知りませんでした。これはゾイサイトという結晶の中にルビーの結晶が含まれた宝石だそうです。
Google画像検索を見ると、アニョライトの雰囲気がわかると思います。結晶の中にRubyが埋め込まれているのです。まさに、このライブラリにふさわしいネーミングだと思います。
使い方
まず、shardsを利用してAnyoliteをインストールします。shards.ymlを編集して、anyoliteを追加します。
dependencies:
anyolite:
github: Anyolite/anyolite
shards install
を実行すると、lib/anyolite/third_party/mruby
に自動的にmrubyがコンパイルされて用意されるようです。(mrubyではなくCRubyを使うこともできるようです。)
例として、次のようなコードを書いてみます。Crystal側で、Kittyクラスを用意して、Anyolite.wrap(rb, Kitty)
として、これをRuby側で呼び出せるようにしておきます。
require "anyolite"
code = ARGV[0]
class Kitty
def initialize(@n : Int32)
end
def mew
puts "mew " * @n
end
end
Anyolite::RbInterpreter.create do |rb|
Anyolite.wrap(rb, Kitty)
Anyolite.eval(code)
end
ビルドして、
crystal build kitty.cr
実行します。引数としてRubyのコードを与えます。
./kitty "Kitty.new(n: 3).mew
うまくいくと、
mew mew mew
と出力されるはずです。実行時にコードを評価してくれていることがわかります。Rubyの戻り値を利用することもできて、
Anyolite::RbInterpreter.create do |rb|
Anyolite.wrap(rb, Kitty)
rv = Anyolite.eval(code)
p Anyolite.cast_to_crystal(rv, Int32)
end
として、
./kitty "Kitty.new(n: 3).mew; 6"
とすれば、
mew mew mew
6
と出力されます。
公式ドキュメント
まだ使い始めたばかりで、全然わかっていないのですが、eval
意外にも、いろいろなマクロが用意されています。
Crystalのモジュール・クラス・メソッドをラップしてRubyから使ったり、変数・定数等を相互でやりとりしたり、Crystal側からRubyのメソッドやブロックオブジェクトを呼んだりすることもできるようです。
call_rb_block
call_rb_class_method
call_rb_method
call_rb_method_of_class
call_rb_method_of_object
cast_to_crystal
does_class_respond_to
does_obj_respond_to
eval
get_cv
get_gv
get_iv
get_rb_class_obj_of
implementation
link_libraries
obtain_given_rb_block
raise_argument_error
raise_index_error
raise_key_error
raise_name_error
raise_not_implemented_error
raise_range_error
raise_runtime_error
raise_script_error
raise_type_error
referenced_in_ruby?
self_in_rb
set_cv
set_gv
set_iv
wrap
wrap_class
wrap_class_method
wrap_class_method_with_keywords
wrap_class_with_methods
wrap_constant
wrap_constant_under_class
wrap_constructor
wrap_constructor_with_keywords
wrap_getter
wrap_instance_method
wrap_instance_method_with_keywords
wrap_module
wrap_module_function
wrap_module_function_with_keywords
wrap_module_with_methods
wrap_property
wrap_setter
ふだんは、もう少しよく理解できた段階でQiita記事に書くことが多いのですが、Anyoliteについては、Crystalでmrubyを使ってevalできるようになったということに感動したので、すぐに記事にしてしまいました。
この記事は以上でです。
-
より正確には、Rubyのコードをevalできるようになる ↩