ローファイ日記

出てくるコード片、ぼくが書いたものは断りがない場合 MIT License としています http://udzura.mit-license.org/

2021年にmrubyを始める皆さまへ

2021年3月5日に、mruby 3.0.0 のリリースがされました。おめでとうございます!

mruby.org

これに関連してなのか、mrubyをこれから始めようとか、ここのところどうなっていますかという質問をちょくちょく受けたり、ツイートを拝見したりするようになりました。

一方で、どうしても情報が古い、あるいは多くのmgemのメンテナンス状況が悪いように見える、などの初学者にとっては難しい状況が広がっており、厳しい気持ちになったり、厳しい感想を述べたりされている方もいるように思います。そして、その感想中には誤解も含まれているようです。

ここでいったん、少しでも「心構え」ができるように、これから触ってみる方々に対しての自分の考えをまとめておこうと思いました。

(さらにいうと、基本的に本原稿はいちユーザ、それもWebインフラに関わるユーザとしての解釈なので、Matzをはじめとした他のmruby関係者と違う箇所もあるだろうと思います。その辺りは適宜突っ込んでいただければという気持ちです)


なお、私はmrubyに関してはいくつかパッチやバグ報告を送ってきた経緯はありますが、基本的にメンテナンスやリリースマネジメントには関わっていない立場です。

ただ、3.0.0のリリース前には、Matz含むコア開発チームに、少しだけお話をさせていただく機会はいただいています。

そもそも、CRubyとどう違うのか

まず、mrubyを触れる方の念頭においてほしいこととして、mrubyはCRuby(MRI)ではありません。違う実装です。ということをまずは覚えておいてほしいというところがあります。

mrubyはCRubyと全く同じことをすることを目的としていません(と私は思っている)。

例えば、組み込みの環境においてはいわゆるUNIX的なファイルシステムが用意されていない場合もあります。したがってmrubyはファイルやIOに関するクラス群を外した形でビルドができたりします。CRubyにおいて、ファイルやIOのような基本的なクラス群を無効化することは困難だと思います。「どの部分を最低限のコアにするか」の考えが違うんだということは理解しないと、不要な戸惑いを覚えることになるかもしれません。

その上で、組み込みで使いやすいか、Rubyらしく生産性が上がるか、という点を考慮して、

を決定しています。また、上記の上から2番めまでの部分までの線引きは、RubyのISO上での仕様である ISO/IEC 30170:2012 を考慮しています。よく、「mrubyはRuby 1.9互換だ」という情報がありますが、ここは、ISO/IEC 30170:2012の使用がほぼRuby 1.9相当なので言われているということです。 Ruby 1.9相当の古い仕様のまま開発されているという訳ではありません 。CRubyの便利機能は、mrubyの目的に反しない範囲で随時ポートされます。

たとえばキーワード引数や「ぼっち演算子」、Fiberのような便利なクラスはmruby 2.1の段階で利用できますし、mruby 3では1行defなどもサポートしています 。一方で「Integerの統合」のような仕様は、2.1の段階ではFixnumが残存し、3.0で統合されるようになった、などタイミングの違いもあります。

この辺りをどの仕様をどのtierに入れるかという「塩梅」は、私の考えでは最後の最後の詰めの部分ではMatzの感性に拠っているところがあるように思います。ただ、その点はそもそもCRubyであってもそうなので、そういうものだと思いますし、私は必ずしも否定的には考えていません。

CRubyとmrubyの違いについて、「Webで使える mrubyシステムプログラミング入門」の23ページにある図を引用します。

f:id:udzura:20210311200354p:plain

個人的なmrubyの強みの理解

mruby に関する情報は前述したとおりまだまだ少ない、古いものも混在するという感じで、基本的に勉強はタフだと思います。ここで変にごまかして初学者おすすめ!と言ってしまうのは私の性格上できない*1

ただ、mrubyはハードウェア系はもちろんですが、ソフトウェアへの組み込みにおいて特異な位置付けを持っており、その点が気にいった方には面白い武器になると考えています。

mrubyをビルドすると、中間成果物としてアーカイブファイル libmruby.a が生えてきます。以下はLinuxでの例です。

$ find build/host -name 'lib*.a'
build/host/lib/libmruby_core.a
build/host/lib/libmruby.a
build/host/mrbc/lib/libmruby_core.a

この libmruby.a が何者かという話をします。

mrubyは、ビルドのプロセスの間に、当然ながらコアに必要なC言語のソースを一通りオブジェクトファイルにコンパイルします。実はそれと同時に、Rubyスクリプトファイル(拡張子が.rbのいわゆるRubyのファイル)を、mrubyの中間表現にコンパイルし、それをC言語の配列にデータとして埋め込み、それをコンパイルしたオブジェクトも生成しています。

どういうCのファイルが吐かれるかは、同じく一緒に生成される mrbc というコマンドを利用すれば分かります。

$ cat hello.rb
module Hello
  def hi
    "hi"
  end
end
puts Hello.hi
$ ./bin/mrbc -B sample -S hello.rb
$ cat hello.c
#include <mruby.h>
#include <mruby/proc.h>
#include <mruby/presym.h>

#define mrb_BRACED(...) {__VA_ARGS__}
#define mrb_DEFINE_SYMS_VAR(name, len, syms, qualifier) \
  static qualifier mrb_sym name[len] = mrb_BRACED syms

static const mrb_pool_value sample_pool_2[1] = {
{IREP_TT_STR|(2<<2), {"\x68\x69"}},
};
static const mrb_code sample_iseq_2[9] = {
0x35,0x00,0x00,0x00,0x51,0x02,0x00,0x39,0x02,};
mrb_DEFINE_SYMS_VAR(sample_lv_2, 1, (MRB_OPSYM(and), ), const);
static const mrb_irep sample_irep_2 = {
  //...
};
//...
static const mrb_code sample_iseq_0[24] = {
0x13,0x01,0x61,0x01,0x00,0x62,0x01,0x00,0x14,0x01,0x1f,0x02,0x00,0x2f,0x02,0x01,0x00,0x2f,0x01,0x02,
0x01,0x39,0x01,0x6b,};
//...

さらに、この、CとRubyそれぞれのファイルをオブジェクトファイルにコンパイルするパイプラインは、利用のために指定したmgem全てに対しても行われます。

そして最終的に、それらのオブジェクトファイルをまとめてリンクし、 libmruby.a アーカイブに固めます。

ここまでの話をまとめると、mrubyは、以下の3つの含まれたアーカイブ一式を固めることができ、そのアーカイブをリンクすれば自分だけのlibmrubyを様々なプログラムに組み込める、という点が特徴的だと考えています。

  • mrubyプログラムの実行に必要な環境一式(パーサ、VM実行機など)
  • mrubyの基本的なランタイムライブラリ
  • 追加で依存しているmgemライブラリ

いわゆる mruby コマンドも、 libmruby.a を組み込んだただのプログラムに過ぎない 、という理解ができると、mrubyの性質の理解が進んだと言えるかもしれません。

そしてこの libmruby.aApacheモジュールにリンクしたら mod_mruby になるし、 nginx にリンクしたら ngx_mruby になる、ということになります。

こういった、実行に必要な全てをアーカイブに固める機能は実はLuaやMicroPythonも備えているそうですが、mrubyはRubyらしい柔軟な文法やDSLを使いたい場合であったり、あるいは洗練されたmrubyのC APIを用いて効率的にバインディングを書きたい場合に非常に力を発揮すると思います。

一方で、とくにWeb開発者でCRubyを中心に書いてきた人の場合、この libmruby.a の用途がピンとこないこともあるかもしれません(私はそうでした)。そういう方は簡単にでもC言語、特にLinuxなどのシステムプログラミング用途のC言語を勉強すると理解が進んだり、何かアイデアが浮かぶのではないかと思っています。

これから学ぶ皆様へ

長々と書いてきましたが、実はWebやインフラ分野でのmrubyは用途がニッチだと考えていて、しかも意外と競合が多かったり(LuaやMicroPython、システムプログラミング分野ではそもそもGoとRustという強烈な競合がいます)、どうしてもC言語(あるいはC FFI)の知識がないと強みを生かしきれなかったりして、微妙にユーザが伸びなかったり、離れてしまったりしている現状があるように思います*2

それでも、「システムの深淵にRubyを決める」という極めてアツいプログラミング体験ができるので、私のような開発者にはたまらない魅力を持った言語だと考えています。

また、mgemのエコシステムは上述のようになかなか厳しい状況がありますが、それでも意思を持って保守しようと考えている開発者はたくさんいますし、P/Rが来たら対応してくれる方はもっと多いです。

そしてなにより、mruby 3が出たとおりMatz始めコア開発者の情熱はまだまだ衰えておりません。この事実は大きいと思います。

これから始める皆様への情報としては、コミュニティめいたものとして、ruby-jp Slackの #mruby チャンネルがあります。(日本語で)アクティブな開発者と交流しやすい場所は今はここではないかと思います。まずは、Twitterなどに愚痴を垂れ流すよりはそこに参加して、相談や議論、モヤモヤの表出をしていった方が生産的かな? などと思っています。

ruby-jp Slackへのリンクを置いておきます。

ruby-jp.github.io

まとまらないまとめ

mrubyをめぐるエコシステムは若干縮小してしまいましたが、mruby コアはまだまだやる気があります。ここから、アクティブな人たちで新しいエコシステムを作っていくタイミングなのかもしれません。

もちろんオープンソース、コミュニティドリブンなので、やりたいようにやるしかないのですが。

そして最近出たmruby 3はメモリフットプリントの大幅改善などを達成した極めて良いリリースです。この記事を機会に、mrubyの面白い用途を探してくれる方が増えると嬉しいです。


[PR]

実は色々この記事に書いた内容をもうちょっと丁寧に書いたのが「mrubyシステムプログラミング」だったりしますので、手にとってみてくださ...い...

電子の場合、Kindleもいいですが本の森でPDF/EPUBが買えます! あと、実は電子は二色刷りです!

https://book.mynavi.jp/manatee/c-r/books/detail/id=119864

というか、PR目的ではないんですが、そもそも私のmruby体験については「この本に大体書いた」という面があり、誘導せざるを得ない。自由に書かせてくれた編集さんのためにも宜しくおなっしゃす。

*1:石井さんやmrubyフォーラムの皆様ごめんなさい...

*2:このニッチさゆえによりハード制限が厳しい組み込みのためにmruby/cが生まれていたりしますが、また別のお話です...