LoginSignup
5
3

More than 1 year has passed since last update.

💎 Crystal言語でも `eval` できる! Anyolite 🟢🔴 がすごい

Last updated at Posted at 2022-06-12

はじめに

Crystal言語では eval を使うことができません。

もしもCrystal言語で eval を使わなければならないとしたら、そのためにはCrystalそのものを実行ファイルの中に組み込まなければならないからです。

Evalはバッドプラクティスとされることが多く、なるべく利用しない方がよいと言われています。しかし、Crystal言語を使っていると、Rubyのevalが恋しくなることがありますね。そんなときに、なんとmruby(やCRuby)をCrystalの実行ファイルに組み込むことによって、Crystal言語でもevalを実行できるようにしてしまう1、というスゴいプロジェクトを見つけたので紹介します。

Anyolite

アニョライトって知っていますか? 私は知りませんでした。これはゾイサイトという結晶の中にルビーの結晶が含まれた宝石だそうです。

image.png

Google画像検索を見ると、アニョライトの雰囲気がわかると思います。結晶の中にRubyが埋め込まれているのです。まさに、このライブラリにふさわしいネーミングだと思います。

使い方

まず、shardsを利用してAnyoliteをインストールします。shards.ymlを編集して、anyoliteを追加します。

shard.yml
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できるようになったということに感動したので、すぐに記事にしてしまいました。

この記事は以上でです。

  1. より正確には、Rubyのコードをevalできるようになる

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3