プログラミング言語 Ruby30 周年記念イベント レポート

プログラミング言語 Ruby30 周年記念イベント

2023 年 2 月 25 日、Ruby 誕生 30 年を記念したイベントが開催されました。 2020 年から流行した新型コロナウィルス感染症の影響で、一時期のイベントはすべてオンラインでの開催が主流となっていました。 本イベントも当初はオンライン形式で予定されていましたが、当日は松江オープンソースラボをメイン会場としてオフラインとオンラインのハイブリッドで開催されました。

集合写真.png

開催日
2023-02-25 (土) 13:40 - 17:30
開催場所
松江オープンソースラボ / YouTube 配信
主催
一般財団法人 Ruby アソシエーション / 一般社団法人 日本 Ruby の会
公式ページ
プログラミング言語 Ruby30 周年記念イベント

進行 :前田修吾

公式ハッシュタグ
#ruby30th
動画
アーカイブ動画

松江会場.jpg

オープニング (文字起こし)

スピーカー
角谷信太郎 (一般社団法人日本 Ruby の会 理事)
スライド
https://speakerdeck.com/kakutani/ruby30th-opening

Ruby30 周年のイベントにあたって、まず、アンチハラスメントポリシーがあります。コミュニティーに関わるそれぞれの人が一人の人間として属性やバックグラウンドによらず排除されることなしに、安全で敬意を伴った実りあるコラボレーションとしての場というのは一人一人が頑張って維持していかないといけないものですので、ご協力よろしくお願いします。

この場を借りまして、昨年の 12 月に亡くなられた Chris Seaton さんのご冥福をお祈り申し上げます。Chris さんは技術的にはもちろん、コミュニティーの皆さんへのリスペクトをすごく大切にしている方で、このような大事な Rubyist をなくしたのは残念な気持ちです。Chris さんと個人的な交流はなかったのですが、RubyConf とかで TruffleRuby の自慢を朗々とやった後にチームのみんなをメンションしてたのがよかったし、RubyKaigi 2021 年ではキーノートをやっていただいて、Object Shapes の考え方をすごく丁寧に解説してくれてて、これからどうなっていくんだろうなという気持ちでいたので、すごく残念です。皆さん、今いる人たちと仲良く交流してやっていきましょう。

オープニングを始めます。Ruby30 周年おめでとうございます。申し遅れましたが角谷と申します。Rubyist-ist を名乗っております。これは Rubyist が好きという意味です。一般社団法人 日本 Ruby の会の理事をやっています。他には昔勤めていた永和システムマネジメントの顧問をやったり、まつもとさんも評判いいらしいよと言ってくれているフィヨルドブートキャンプというスクールの顧問をやったり、RubyKaigi では Se ñ or Organizer という肩書きでやっております。今年の RubyKaigi は 5 月に、「松本にまつもとさんを行かせる」という松田さんの壮大な冗談で、松本で開催します。皆さん来てください。今年 RubyKaigi がリブートして 10 年になります。ここまで松田さんとそらはさんが繋いで育ててくれて、ありがとうございます。 松本はまちが綺麗だし、まちの中に水が無料で湧いてるんですよ。そばも美味しいのでぜひ来てください。今年も絶対楽しいと思います。

個人のプロジェクトで、Ruby の入門書の先っていうので Jeremy Evans が書き下ろした本を翻訳しています。ぜひ買ってください。

30 年前、1993 年、皆さん何していましたか。ジュラシックパークが 30 年前なんですよ。当時僕は学割でジュラシックパークを見ていましたが、彼女の方が僕より Unix 詳しかったです。今見たら「どうなってるのこれ」となりますが、一方 Ruby は 30 年経っても迷走せずに安定していてすごいなと思います。あとは「男はつらいよ」が、バブルが弾けてみつおが就職しづらい回で、Ruby の誕生とも関係する、世相を反映した年でした。

みなさんは Ruby のどこからやってきましたか。僕は確か 20 世紀の終わり頃でした。当時通勤経路に池袋のジュンク堂があって、毎日通ってたんですよ。6 階に毎日のように行ってて、だいたい 2000 年ごろに『オブジェクト指向プログラミング言語 Ruby』『XP エクストリームプログラミング入門』『達人プログラマ』にまとめて出会ったんですよね。98 年くらいに就職してたんですけど、コンピュータとかプログラミングはいいなと思っていたのに、仕事があまり楽しくなかった。なんなんだこれ、と思っていて自分がおかしいのか世の中がおかしいのかわからなかったんですが、この 3 冊に出会って、世の中がおかしいと確信を持って……ということは自分がおかしくなったのかもしれないですね (笑) そこから今に至る旅が始まります。ケントベックは「プログラマもちゃんとした人間になれる」「今までいた世界がおかしかったのかもしれないよ」と言ってくれている。その結果 Ruby コミュニティーの一員として、Ruby を使っているチームがアジャイルになっていく手伝いをしていて、内なる平和を得ました。

今日、2023 年。気づいたら僕はずっと同じ話をしているんですね。「ワールズ・エンド」という映画があって、主人公は高校の時の思い出を 23 年引きずっている無職の人で、まちを出た同級生に会いに行ったらみんなすごく偉くなっている、という話です。今日の皆さんの話を聞いていると、周りはすごくなっているのに自分はずっと同じじゃんみたいな気持ちにながら、今日のオープニングの話をしていました。

今日一日楽しんでください!

特別講演: Ruby の 30 年 (講演まとめ)

スピーカー
高橋征義 (一般社団法人日本 Ruby の会 代表理事)

高橋さんが Ruby の歴史をまとめた「年表」(https://github.com/takahashim/ruby-history)

Ruby の 30 年

30 周年おめでとうございます

30 年を振り返ると、過去 25 年で、Ruby が広く普及してきました。ちょうど真ん中の 2008 年に Ruby1.9 がリリースされたり、Merb が Rails に取り入れられたりしました。 直近 5 年では、今後の飛躍に向けた改善がなされていると言えます。

もうちょっと詳しく

1993 年 ruby が誕生しました。しばらくの間はユーザーはまつもとさん自身でした。

1995 年 ruby が初めてネットニュースに載りました。

1997 年 プロクラミング言語に関心のある層にリーチしていきました。初めてインターネットメディアに (Internet Watch) に取り上げられました。https://internet.watch.impress.co.jp/www/article/970922/ruby.htmTRY!PC 11 月号に「ちょーわかりやすい! Perl & ruby 入門」。この時、「Perl」と並べた時のバランスを取るために「Ruby」と大文字始まりの表記にすることになったと言われています。オンラインソフトウェア大賞 97 に Ruby が入賞しました。

1999 年 ruby-lang.org 取得。世界初の Ruby の書籍・『オブジェクト指向スクリプト言語 Ruby』 が刊行されるなど、Ruby がより広く知られていきました。

2000 年 Ruby に関する洋書“ Programming Ruby(Dave Thomas and Andy Hunt) ” が刊行されました。 2001 年には 1 回目の RubyConf が開催され、Ruby が世界にも知られていきました。

Ruby on Rails が登場し Ruby のエコシステムに大きな変化をもたらします。

2003 年に RubyForge.org が公開されました。その頃の様子は シリーズ パッケージマネジメント 【第 1 回】 RubyGems (1) で垣間見ることができます。Ruby 標準のパッケージマネージャーがない中 Rails が RubyGems を採用していたことで、最終的には RubyGems が公式パッケージな位置付けとなりました。GitHub が Gem 専用のホストをしていましたが、2009 年に Gemcutter に移行します。 Gemcutter は 2010 年 3 月に RubyGems にリネームされます。 2010 年 9 月には Bundler もリリースされ、 RubyGems と Bundler でパッケージ管理が行われるようになりました。

その頃 Merb が取り込まれたバージョンである Rails 3.0 が公開されました。Rails は多くのライブラリを巻き込んで動かすため、パッケージ管理に与えた影響が大きかったのだろうと思います。Rails の開発とともに Ruby のエコシステムも構築されていきました。

30 周年に寄せて (文字起こし)

そんな感じで 30 年が経ちました。感想を一言で言うと「生き残っていて嬉しい」です。30 年も経つと、動かすだけでも大変だったり、メンテされていたとしても新しいものはリリースせず既存で動くものが動くようにメンテナンスするものもある中で、30 年経っても動いているだけで嬉しいわけです。しかもだんだんより良くなっています。それは、処理系実装者の皆さんの尽力の賜物ですし、ライブラリ開発者の皆さん、ドキュメント・情報提供などコード以外の活動をしている皆さん、Ruby を採用している企業、Ruby のプロダクトや開発者を支えているみなさんのおかげです。30 年間、Ruby は多くの人に支えられて成長してきたし、多くの人を支えてきました

今日は 30 年の節目の記念すべき一日という位置付けではありますが、30 年はこれまで続いた歴史・発展の通過点とも言えます。Ruby の今後が楽しみです。

招待講演 1: Enterprise Platform SaaS における Ruby の 12 年 (文字起こし)

スピーカー
なひ (Treasure Data CTO)

自己紹介、会社紹介

30 周年おめでとうございます。 Ruby 歴は 24 年くらいで、私が Ruby を触り始めたのは 1999 年でした。その頃仕事では Perl を使っていました。使い始めた時の Ruby のバージョンは 1.2.6 で、しばらくは Ruby ユーザーだったが、2003 年くらいに Ruby1.8 で標準ライブラリを大幅に追加しようという話があって、そのいくつかを提供する中で標準ライブラリのメンテナー、Ruby のコミッターとして Ruby1.9、2.0 の頃まで活動していました。途中から JRuby の OpenSSL を全部作り直して動くようにするということをやっていました。 2014 年に Treasure Data に入社し、現在は CTO をやっています。そこから先は Ruby ユーザーとして Ruby を使っています。

Treasure Data は 2011 年にシリコンバレーで創業した、現在は CDP (Customer Data Platform) という Enterprise SaaS をやっている会社です。Data を使って Intelligent な Foundation を作って Human Life にコントリビュートしようというビジョンを掲げるなかで、今は CDP をやっています。エンジニアの中に Ruby コミッターが 4 人います (@nalsh、 @spikeolaf、 @k_tsj、 @mineroaoki)。少し前まで @k0kubun もいました。 今日は Treasure Data が創業してから 12 年、どのように Ruby を使ってきたかという話をします

Treasure Data の現在のシステム構成

Ruby を使っているところ以外は、UI は JavaScript で、クライアント SDK は色々な言語を使っています。クラウド上では、AWS のサービスや、Java / JVM で動いているものが多いです。それでも Ruby を使っている部分は多い方なんじゃないかと思います。

規模としては、サービスチームが 20、システムコンポーネントが 30、リージョンは 5 つ。デプロイしているものでは、クラスターが 550。ECS インスタンスは 3200〜3500、さらに Lambda や Flink アプリケーション、pods などいろいろあります。

プロファイル (CDP のお客さんが集めた個人情報) がシステム上には合計 700 億件。そのプロファイルをマーケティングなどに活用するのが月に 100 ビリオン。取り込んでいるイベントが月に 3000 ビリオン。処理しているレコードは月に 6000 トリリオンです。システムはすべてマルチテナントで作っています。

2011 年〜 Hadoop as a Service

2011 年当初は、Hadoop as a Service でやっていました。Ruby30 周年のパンフにも載っている fluentd をクライアント SDK として提供して、ユーザーにインストールしてもらい、色々な情報のソースをプラグイン実装で組み合わせて Treasure Data のクラウドにログを送信できるようにしました。同様に、Hadoop を API 経由で実行して収集したログを解析できる CLI を提供しました。どちらも gem として配布しました。 クラウド側は、Rails アプリが 1 つドーンとあって、API と UI がそこに実装されていました。Hadoop のプロセスの実行、プライオリティ制御やリトライ制御などの分散ジョブキューを MySQL と Ruby で実装していて、今でも走っています。それを元に、Hive ジョブの JVM プロセスを fork & exec で動かすと。分散ジョブがある点がちょっと違うかもしれませんが、スタートアップが Ruby On Rails で SaaS を作りますというとこんな感じになる、標準的な構成かなと思います。

2013 年〜 Big Data Platform

2013 年からは Big Data Platform としてやっていました。この頃、JavaScript SDK を作って、それまで fluentd でログを収集して分析してという形だったものをブラウザから直接イベントを収集できるようにしました。サーバーに fluentd を置いて、HTTP のインプットプラグインとして Nginx の裏で受けて、イベントをバッファリングして、コンパクションして、アグリゲーションした結果をストレージに格納するという仕組みで、それを Ruby で実装しました。具体的にどのようにやっているかは、スライドのリンク集に書いてあります。

その頃、fluentd がマイクロバッチでデータをストリーミング転送するものなのに対して、バッチでトランザクショナルにリライアブルに転送するために Embulk というのを OSS で開発しました。色々な種類のソースをプラグインで組み合わせて使う点は fluentd と一緒です。Embulk は Java で実装しました。しかし「プラグインを Ruby でも使うようにするのはどうだろう」ということで JRuby を使ったところ、色々なプラグインが Ruby で出てきて、Embulk も広まっていきました Treasure Data としてはそれをクライアント側にもサーバー側にもおいて使っていました。

2015 年〜 DMP:Data Management Platform

2015 年頃からは Data Management Platform として機能を拡張していきました。その時増えたのが Digdag です。この頃は Treasure Data の中でも「ハイパフォーマンスなものは Java で書くよね」というのがあって、Digdag は Ruby では書かれていません。Embulk も最初は Java で書いていて、Embulk を作って、Digdag を作って、データマネジメントのパイプライン管理をしていました。その頃同時に、JS で作っていた UI 部分を Rails から分離しました。要因の一部として、北米で人を採用して Rails で UI 開発するのは辛くて、グローバルチームで UXxUI 開発ができるようにしたかった点があります。他には Rails Admin で管理コンソールを追加しました。これはどこでもやっていることかなと思います。

他には、これはこの時期ではなくて当初からやっていたことなんですが、Treasure Data の全ノード、全コンテナに fluentd が乗っていて、Nginx のログとか、Rails のログとか、アプリケーションのログをすべて Treasure Data に送るというドッグフーディングのようなことをしていました。集めたログをメトリクスや障害の調査、分析のサポート、プロダクト向けのデータ分析に使っていました。それだけではなくて、アグリゲーションしたシステムメトリクスをメトリクスサーバーに送ることもしています。

2017 年〜 CDP:Customer Data Platform

2017 年に CDP にピボットしました。CDP が何かというのはリンク先の、青木峰郎さんが書いてくれた資料を読むとよくわかるので、興味がある人は見てください。Treasure Data のユーザーが作っているデータのワークフローやデータパイプラインを見ていると、どうやら、Treasure Data のお客さんの顧客のデータを集めて分析してマーケティングに活用するサービスを各自で作っているということがわかりました。だったら Treasure Data 上でそういうサービスを作りましょうということで、Rails で CDP API をガーっと作り、ピボットしたと。

その頃、認証とアクセスコントロールの API を Rails 製のデータプラットフォームから Rails のまま分離しました。なぜやったかというと、この 2 つと他の API の負荷トレンドが異なるのと、これらが落ちると他が全部止まるので、切り出すことでオートスケーリングを容易にするためです。「ある処理は時間がかかる」「ある処理はメモリを食う」というように負荷が均質ではないとオートスケーリングは難しいが、「認証だけ」とか「アクセスコントロール」のように切り出せば負荷が平準になるのでオートスケーリングが容易になる、ということで切り出しました

2019 年〜 CDP:Customer + IoT

2019 年 Treasure Data が Arm 社に買収されて Arm と一緒に IoT もやろうということで CDP に加えて IoT をやっていました。そこで、これまで Ruby で作って運用していたものからストリームインポートの大部分とジョブコントロールの一部を少しずつ切り出して Kotlin で再実装していくことになりました。ジョブコントロールについては今も、Kotlin 以外の JVM のものも含め、少しずつ再実装が進んでいます。IoT 向けの投資があったことだけでなく、これまでより 1 桁 2 桁多いデータ量を扱うことになったのでスケーラビリティとリライアビリティの観点から JVM を選んで再実装していったという流れになります。

その中でも、オペレーショナルなデータアクセス用の API の新しいビジネスロジックは Rails アプリとして追加していきました。

また、これもあまり楽しくない話をしなければならないのですが、CDP で集めたデータを、ユーザーがクラウド上で分析したりマネジメントしたりするための実行環境を提供していて Python のみをサポートしています。Ruby で提供できないわけではないのだけど、データサイエンスやマシンラーニングをやる人はみんな Python を使うので、それを反映している形です。

2021 年〜 Customer Data Cloud

2 年前の 2021 年から、CDP で集めたデータを他のエンタープライズシステムと連携してお客さんがもっと便利に使えるようにしようということをやっています。そのために追加する新しいアプリケーションのビジネスロジックは Rails を使っています。ビジュアライゼーションとか、リソースプロビジョニングなどです。

あとは Treasure Data がマシンラーニング用に使うエンジンを作って、それをユーザーが使えるようにする機能を現在開発中です。これはやはり Python を使っています。ユーザーがコードを書くわけではないので Python でなくても良いのだが、マシンラーニングのエコシステムやグローバルな開発者確保を考えると、Python になってしまう。

まとめ

Treasure Data は Ruby とともに 12 年やってきました。ビジネスロジックの実装は今でも新しく作るものはほぼすべて Rails です。これは Ruby の開発者の皆さん、ドキュメントも含めライブラリの開発者の皆さんが作っているエコシステムに完全に乗っかっている形。ビジネス要件は当然変わる、というか増えていく。それに追従して実装を変更していくことを考えると、Ruby や Rails の開発者にフォーカスしている強さ、立ち上がりの速さや開発者もコードも柔軟性が高い点からは離れ難いなというのが我々の考えです。2015 年か 2017 年かに、日本以外で Rails 開発者の確保に苦労した時期はあります。今は北米でもヨーロッパでも苦労することはないので、困らなくなったかなというのはあります。ただ、日本にいると「Rails ができる。かつ、分散データシステムに強い」エンジニアがそれなりにいるので、そこら辺が違う。分散データシステムもわかる人がビジネスロジックを書けるのは強くて、ある程度日本のエンジニアに頼っているところはあります。

マイクロサービス化した API は JVM になりがち。マルチテナントで、いつ需要があるかわからないので、オートスケーリングがよくチューニングされていることが重要。そのためには負荷が均質であることが必要。均質な部分を切り出そうとすると Ruby の柔軟さより、型がある JVM の効率性が重要になってる。認証やアクセスコントロールなどの柔軟性が重要なものは引き続き Rails でクライアント側のキャッシュを使うことで問題を回避しています

データサイエンスやデータマネジメントでは開発者確保の理由から Python を採用している。これが開発体験を重視するような流れになったら Ruby にできるかもしれません。

最後になりましたがもう一度。 Ruby30 周年おめでとうございます。 Ruby コミッターの皆さん、コミュニティーの皆さん、いつも継続的な開発をありがとうございます。それでは発表を終わります。

ライトニング・トーク (各トークまとめ)

オンライン形式でしたが、一人 5 分、コロナ禍で数年間機会が減ってしまっていた懐かしの LT 形式でした タイマーは、時間が来たら銅鑼がなる、修吾さん作 Ruby Wasm 製アプリでした。

LT.jpg

ruby とブロックチェーン (仮)

スピーカー
素数

Python で数理シミュレーションをしている素数さんが、Ruby で素数判定を使った暗号理論の発表をされました。

Ruby Everywhere すべてが Ruby で書ける世界へ

スピーカー
Hir0_IC

ポータブルな Ruby である mruby によって“ Ruby Everywhere (どこでも Ruby) ”が実現している話と、これまでの 30 年、mruby、mruby/c がそうであったようにこれからも Ruby とともに進化していくだろうという発表でした。

RuboCop メンテナーしぐさ 2023

スピーカー
伊藤浩一
スライド
https://speakerdeck.com/koic/rubocop-philosophy

誕生当初代統一フォーマッターを目指していた RuboCop は、今ではコミュニティー内のさまざまな好みや事情を反映し、Ruby Style Guide をデフォルトとしつつカスタマイズできるようになっています。RuboCop メンテナーとして多くのユーザーにとっての利便性を一番に考えて活動している話や、理想の RuboCop はどのようなものだろうかという内容でした

初刷り 何冊買いました?

スピーカー
Masatoshi Seki

「初刷り買えます」とは、ご自身の著書である『dRuby による分散・ Web プログラミング』をアピールする、RubyKaigi ミーム。RubyKaigi 2006 での「初版買えます」から RubyKaigi 2022 に至るまでの登壇とともに「初刷り 何冊買いました?」に至るでの歴史。かさばらないので電子版『dRuby による分散・ Web プログラミング』を買おう。

Ruby (MRI) の好きなところ 30

スピーカー
塩井美咲 (しおい)
スライド
https://speakerdeck.com/coe401_/ruby-mri-nohao-kinatokoro-30

タイトル通り、しおいさんの、Ruby の好きなところを 5 分で 30 個 (!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!) 発表する内容でした。

Python のこの機能が Ruby に欲しい

スピーカー
うなすけ
スライド
https://slide.rabbit-shocker.org/authors/unasuke/ruby30th-lt/

Ruby アソシエーション開発助成金 2022 に採択された、Python 実装のaioquic を Ruby に移植する取り組みの中で、Python にあって良いなと思った機能、逆に Ruby にあって良いなと思った機能についての紹介でした。

Red Data Tools で切り開く Ruby の未来

スピーカー
mrkn
スライド
https://speakerdeck.com/mrkn/red-data-tools-deqie-rikai-ku-ruby-nowei-lai

Ruby 用データ処理ツールを開発するプロジェクト・Red Data Tools の活動概要と、Ruby 用データフレームワーク、Red Amber、 Polars RubyDarupandas の紹介でした。興味がある方は Red Data Tools で一緒に開発しましょう!

たのしい String

スピーカー
ima1zumi
スライド
https://speakerdeck.com/ima1zumi/tanosiistring

Encoding 入門話に始まり、GitHub の Slack 用アプリで実際にあった文字化け不具合を再現してみたり、Encoding#replicateでエンコーディングを追加してみたり、Encoding#replicateは ima1zumi ご自身の gem であるRubyEncodingIroha でしか使われていなくて Ruby3.3 では無くなってしまうという完璧なオチまでついた、Encoding がよくわかるお話でした。

Ruby でメシを食う

スピーカー
こしば

以前は Ruby でどう仕事をしていくかということを話していたが、2000 年代〜2010 年代にやっていた Ruby コミュニティーでの活動が縁となって仕事に繋がっているなと感じておられることや、パートナーである小芝美由紀さんは『現場で使える Ruby on Rails 5 速習実践ガイド』 の著書の一人で、家族ぐるみで Ruby に関わり Ruby でメシを食っているなという話でした。

Ruby でハローワールドする 30 の方法

スピーカー
大倉雅史
スライド
https://speakerdeck.com/okuramasafumi/30-ways-for-hello-world-in-ruby

タイトル通り、Hello World を Ruby で出力する方法 30 個のご紹介でした!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Ruby 30 年の歴史に思いを馳せる

スピーカー
mame

Ruby 全バージョン (0.49〜3.21 の 215 バージョン) で動く Quine を作った話。バージョン間の構文違いなど差分はあれどちょっと (⁉︎) なんとかすると完成したそうです。 詳しくはこちらにhttps://github.com/mame/all-ruby-quine

招待講演 2: Ruby 1.9 から見る風景 (文字起こし)

スピーカー
園田裕貴 (Yugui) (Niantic / Staff Software Engineer)

はじめに

Ruby30 周年おめでとうございます。 Yugui と言います。Ruby1.9.1 / 1.9.2 のリリースマネージャーをしていました。 仕事では Ruby を使っておらず、Ruby の開発にも携わっていないので、今日は当時の昔話をするつもりです。最後に少しだけ最近の話題も盛り込めたらと思っています。

Ruby1.9 の頃

高橋さんの話でもあったように、Ruby1.8 から 1.9 というのは、文法的に、ライブラリ的に、エコシステムとして、すべてが変わっていった大変更の時代でした。また 1.8 の頃から Rails が圧倒的な存在感を放っていて、それがますます増していった時代でもありました。

Ruby1.9 で入った変更というのは具体的には、言語本体の評価に関する仕様変更 (YARV / Native Thread)、 組み込みライブラリの変更 (文字列エンコーディング)、添付ライブラリの変更 (RubyGems / Oniguruma / Ripper)、 課題管理システムの導入 (bugs.ruby-lang.org)、どういったプラットフォームをどういう優先付けで開発するかというサポートレベルの設定などです。

実際、年表にまとめてみると、Ruby1.9.1 以前というのは、Rails の登場、RubyKaigi の始まり、Rails が業界スタンダードになっていった時代というのがわかります。

変更にはもちろん理由があります。1 つは、Ruby を使いやすくするにはどうした良いか、Ruby の中をよくするにはどうしたら良いかという技術的な課題。もう 1 つは Ruby を開発する仕組み上の問題を解決するにはどうしたら良いかという運営上の課題。これらを解決するために大変更が行われました。

Ruby1.8 の頃の技術的な課題と解決策

当時の技術的な課題はだいたい『Ruby ソースコード完全解説』に書いてあります。それ以外には GC、評価器の速度、ユーザースレッド、多言語化、パーサの再利用、正規表現エンジン、ライブラリ配布といった課題がありました。これらに対し Ruby1.9 以降、世代別 GC ・ Copy GC、YARV、Native Thread、Encoding、Ripper、Oniguruma / Onigumo、RubyGems / Bundler といったもので解決がなされていきました。そのうち 1.9 時点で YARV、Native Thread、Encoding、Ripper、Oniguruma、RubyGems が導入されました。

まず、評価器の速度について。Ruby1.8 までは、AST の Node を再帰的に評価する仕組みでした。しかし Ruby が広く使われるようになりユースケースが広がると、そのやり方だと遅いと問題になっていきました。そこで YARV が導入され、AST を仮想マシンコードにコンパイルしてから実行する仕組みに変わりました。

次に、ユーザースレッドについて。それまでの Ruby にもマルチスレッドの仕組みはあったが OS から見るとあくまで 1 つのスレッドで、その中でコンテキストを切り替えることで擬似的にマルチスレッドを実現するものでした。OS から見ると 1 つのスレッドでしかないのでマルチコアで実行することができません。そのためマルチコアを活用するにはマルチプロセスにするしかない。しかしプロセスを増やすとリソースを消費するし管理も大変になるという問題があります。Ruby のスレッドと OS のスレッドを 1:1 でマップすればマルチコアの利点を活用できる。という大変更が入りました。

多言語化について。当時の Ruby のエンコーディングで重要なのは KCODE で、KCODE にエンコーディングの名前を代入するとすべての文字列オブジェクトの挙動が変わるという仕組みでした。個々の文字列オブジェクト自身はエンコーディングを持っておらず、ただのバイト列だったと言えます。文字リテラルも、対応するバイトを返していました。これだと、1 つのプログラムの中で異なるエンコーディングのデータを扱うのが大変です。また KCODE が日本語に特化したものだったので、それ以外の言語への対応も大変でした。それに対し、String オブジェクトがエンコーディングを持つように変更が入りました。その結果、エンコーディングが間違っていればエラーを吐く、文字列オブジェクトがエンコーディングを持つので文字単位のイテレートが可能に、external_encodinginternal_encodingで Ruby プロセスの外からエンコーディング情報をとってきたり適切に変換がされるように、また文字リテラルが数字ではなくその文字を表すオブジェクトを返すようになりました。

Ripper や Oniguruma について。irb のように、Ruby プログラムの中で Ruby コードを処理したいことがあります。Ruby コードをパースして処理する必要があるのですが、Ruby コードをパースするのは恐ろしく複雑です。Ruby の文法の柔軟性はパースの大変さでもあります。それまでは Ruby をパースするには自分でなんとかする必要がありました。ここで Ruby 本体の parse.y とパーサーを共用した Ripper が導入され Ruby コードを処理する Ruby コードを書きやすくなりました。さらに Ruby1.8 までの正規表現ライブラリではエンコーディング周りがややこしかったり、現在と比較して機能が限られていたという問題やライセンス周りの問題がありました。そこに Oniguruma が登場し、すごく強力な正規表現ライブラリを使えるようになりました。

重要なトピックとして、ライブラリ配布の話題があります。昔の Ruby ではライブラリをインストールするのはすごく大変なことでした。推移的な依存関係を人手で把握して正しいところからダウンロードする、アーカイブを展開する、正しいロード可能なディレクトリに配置する、あるいは環境変数を設定する必要がありました。親切なライブラリであればそれらを自動化するインストーラーがあったかもしれませんが、それだとライブラリ作者がちょっと大変ですね。さらにいうとライブラリ同士、最新版同士で互換性があるわけではないので、すべての条件を満たすバージョンを特定してダウンロード、インストールしなければいけませんでした。これは本当に大変でした。ちょうど、依存関係を自動解決して適切にインストールできる RubyGems が登場して、Ruby1.9 から本体に添付されるようになりました。

Ruby1.8 の頃の運営上の課題と解決策

運営上の課題で言うと、当時まつもとさんに負荷がかかりすぎていました。つまり、言語の変更をしたいと言われたらジャッジし、Ruby の将来について考え、人と会って話し、さらにパッチを取りまとめて安定してリリースできるバージョンにする。その間にも色々な人が Ruby に変更を加えるので、うまいこと管理し、リリースできるものを作り上げないといけない。明らかに一人がやるには大変すぎていました。そこでリリースマネージャーの仕事を分離していきました。Ruby1.8 では卜部さんがリリースマネージャーをしてくれていたので、1.9.1 / 1.9.2 では私が手を挙げて担当しました。

課題管理という問題もありました。それまで、バグ報告・トラッキング、仕様変更の議論・提案はすべてメーリングリストで行われていました。メーリングリストだと、メールスレッドが流れるといつの間にか忘れられてしまうという問題があります。この問題を解く方法は現代であればみなさんご存知だと思います。Issue トラッカーを入れるといい。そこでいくつか検討した結果 Redmine を採用し、今でいう bugs.ruby-lang.org を立てました。1 つ自慢したいのは、いきなり完全移行するのは無理なので、メーリングリストに報告されたものは Redmine に、Redmine に報告されたものはメーリングリストにも流れるような拡張を書いて適用しました。

それから Ruby はさまざまなプラットフォームをサポートしていますが、それら全部を等しくサポートするのは大変なので、優先度付けをして、とにかくメジャーなプラットフォームだけはしっかりしたものを出す態勢を作りました。

その結果 1.9.1 から 2.x ごろまでで、リリースマネージャー制の確立、Issue Tracker もある、CI もある、GitHub にリポジトリもある、VM で実行する、Native Thread がある、GC に凄いアルゴリズムが導入された、多言語対応ができた。今では当たり前と思うような開発体制、言語機能が確立していました。

残された課題・新たな課題

それでも残っている課題や、新しく出てきた課題もあります。もっと速くしたい、型による利便性が欲しい、スレッド難しい、いろんなところで動かしたい、誰も信用できない、色々なビルドがある、などです。

もっと速くしたい課題については JIT が入り、もう運用されつつあります。型による利便性が欲しい課題については、RBS や型チェッカーで、型解析によって IDE の補完を効かせたりスタティックに検証したりすることができるようになっていっています。スレッド周りは 1.9 で大きく変わりましたが、そもそもマルチスレッドプログラミングは難しい、Ruby のスレッドと OS のスレッドを対応させるとしても OS のスレッドを大量に作ろうとするとリソースを消費しすぎる問題がありました。この辺りは Ractor で解消されつつあります。その後出てきた問題として、もっといろんなところで動かしたい、たとえば「Ruby 以外で書かれたシステムのプラグインとして実行したい」というニーズは当然あります。これに対しては当時は Native Client 対応を入れようとしていました。最近では同じ問題を Ruby WASM が解きつつあります。

誰も信用できない、というのは、サプライチェーン攻撃に関することです。このリスクが近年高まりつつあります。であれば、外部パッケージをインストールするときにそのパッケージの素性みたいなものをきちんと分析したい。パッケージ管理操作の、メタデータを取得する、ビルド手順を検証するなどの段階で信頼できないコードが実行されてしまっては大問題です。RubyGems はこの点はすごく優秀で、仕組み上他の言語ではどうしても信頼できないコードが走ってしまうことがある。RubyGems ではこのような問題はありません。スタティックな JSON だからです。1 つ弱点があって、拡張ライブラリを使っていると、それを取得する段階で信頼できないコードが走ってしまう。ここだけはなんとかしたらいいなと思っています。解決策としてはいくつかあると考えられます。1 つはクロスビルドがもっと簡単だったら、それぞれの拡張ライブラリをあらかじめビルドしておいて、プリビルドバイナリを Gem に埋め込んで配布する選択肢がもう少し現実的になるかなと思います。あるいは、この問題は extconf.rb という拡張ライブラリの仕組みに深く根ざしているので、それに代わるもっと良い何かがあってもいいかもしれません。あるいは、最近 Ruby は速いので、速度が必要なものを C で書かなければならないわけではないです。なので、拡張ライブラリとかやめて Ruby で書けばいいんじゃないでしょうか。

いろいろなビルドという問題もある。今時では、1 つのプロジェクトの中に複数の言語が含まれるのは当たり前です。クロスビルドで配布するのもよく見られます。最近台頭した言語ではクロスビルドはすごく簡単だったりします。コンテナイメージをビルドするのも当然に求められることです。コンテナをビルドする上でビルドは決定論的である、つまり同じソースを与えたらビルドはバイト単位で一致する・同じアウトプットが出てくることが期待されます。これに対しても方向性はいくつかあります。やはり Ruby で書くのはすごく良いことです。クロスビルドが大変とかビルドは決定論的であるとかというのはだいたい拡張ライブラリからくるもので、だったら Ruby で書けば、すべて問題は解決なのではないでしょうか。とはいえ Ruby で書けないケースもあるにはあります。これらの問題に強い Bazel というビルドシステムがあり、これの Ruby サポートがもっと強くなればいいなと思うので、数年前から時折取り組んでいます。

最後に

思い返すと Ruby1.9 は Ruby が現代的になった大きなステップでした。その大きな変更は理由もなくやったわけではなく既知の問題に真摯に取り組んだ結果です。残された問題についても、見てきたように、すでに解かれつつあったりいくつかの方向性が考えられたりします。そんな感じで、Ruby は今も適応を続けている言語で良いんじゃないかなと思います。以上で終わります。

招待講演 3: Ruby で作る最強ゲーミングルーター (文字起こし)

スピーカー
奥一穂 (Fastly Sr. Principal Engineer)

発端

冬休みに入った頃、家族から「自宅にマイクラサーバーを動かしたい」と相談がありました。「パソコン余ってるしいいよ」と言って動かしてみると、「ラグがひどくてみんなで遊べない!」「まじか…」となった。 一般的な対策をググってみると、ブロードバンドルーターの QoS を、ゲームに使う機器の IP アドレスの通信を優先するように設定しましょうと書いてある。でも、ゲーム機って複数ありませんか。パソコンもあればゲーム機もあるし、ゲームで使うパソコンで何かをアップロードしている間、他のゲーム機でゲームしたらどうなるんでしょう。どっちも優先されますよね。やっぱりうまくいかないんです。もう 1 つの問題は、うちのブロードバンドルーターは買い替えたところだったんですが、「この ISP 向けでは QoS は使えません」となっているんですね。ISP の IPv4 対応方法が MAP-E 方式というものだったのですが、これだと QoS が使えなかった。「まじか…」となるわけですね。

自宅マンションのネットワークは、共用部までは光が来ています。共用部に VDSL の集合モデムが置いてあって、そこからは電話線を通じて各家庭までインターネットが来ていると。VDSL の宅内モデムを使ってパソコンにつなぐというかんじですね。https://flets.com/first/kouji/const_apartment.html

具体的な帯域は、ファイバーが自宅まで 1Gbps で来ている。マンションの集合モデムから自宅の VDS モデムまでは下り 100Mbps ・上り 35Mbps。そのあとはイーサネットでブロードバンドルーターに行って、パソコンやスマホまでイーサネットなり Wi-Fi なりでいく、という感じになっています。

fast.com でベンチマークを取ってみました。ダウンロードではアンローデッドのレイテンシが 7ms、ローデッドが 36ms となっています。ダウンロード測定時に負荷があると 36ms、負荷がないと 7ms ということで、負荷がかかると 29ms 遅延が増えているということがわかります。ではアップロードの場合どうかというと、アンローデッドが 8ms、ローデッドが 168ms になるんですね。アップロード中に遅延が 160ms も増えているということがわかります

どうしてこんなに遅延が大きいのでしょう。インターネットが遅いから仕方がないのでしょうか。そうじゃないんです。

輻輳制御とボトルネック

ここで輻輳制御というインターネットの技術の話をします。輻輳制御とはパケットの送信頻度やタイミングを制御するための技術です。これによって、インターネットの帯域を使い切ったり、公平に共有したりします。アルゴリズムの例としては、ロスベースと言われる、昔からの Reno や Cubic といったものが主流です。最近では遅延ベースの BBR というものも出てきているようですが、今日は Reno や Cubic を中心に話をします。

左にサーバーと右に PC があり、パケットが左から右に流れていると考えてください。その間にルーターが 2 つあります。サーバーから 2 つ目のルーターまでは割とパイプが太い (パケット 2 つ分とする) が、2 つ目のルーターから PC の間だけが細くなっている (パケット 1 つ分とする)。サーバーから PC にパケットが届くと PC からサーバーに ack が返ります。またパケットが流れていくと ack が返ります。これがインターネットの通信です。これを続けていくとサーバーがパケットを 1 つ多めに投げるタイミングがあります。多めに投げられたパケットは、最後の細くなったパイプを流れていけないので、直前のルーターのキュー、バッファーに溜まります (1 つ分溜まっている状態)。通信を続けていくとまたパケットが多めに投げられるタイミングがきます。それがルーターに溜まります (2 つ分溜まった状態)。しばらく続けるとまた多めに流れてきて、ルーターに溜まります。これを繰り返してルーターのバッファーがいっぱいになります。それでも通信を続けていくとまた多めに流れてきて、ルーターのバッファーがオーバーフローします。パケットが捨てられるわけですね。ack が順番に返っていて、捨てられたパケットの ack が返らないといけないのだがそれは返らず次のパケットの ack が返るので、その瞬間にサーバーは「パケロスが発生した」と気づくわけですね。パケロスに気づいたサーバーはしばらくはダンマリを決めて、PC から ack が返ってきても次のパケットを投げません。しばらくしてからパケットを投げ始めます。この無通信の間にルーターのバッファーがはけていくので空いたスペースができます。そして通信を続けていくと、またサーバーが余計にパケットを投げてバッファーに溜まる。

バッファーにパケットが溜まっていく、溢れてパケロスが発生し、サーバーが黙る。これを繰り返すのが輻輳制御です。

先ほどの fast.com での結果からバッファーの大きさを計算できます。無通信時は 7ms。92Mbps だったので、7ms で 54 パケットくらい流れていることがわかります。一方でルーターのバッファーの深さは 29ms 分、220 パケットあることが計算からわかります。

輻輳制御はボトルネック直前のバッファーにパケットが滞留します。遅延というのはバッファーの大きさで決まります。

じゃあ、自宅のネットワークのどこにボトルネックがあるかというと、下りは集合モデムから宅内モデムの 29ms、上りは宅内モデムから集合モデムの間の 160ms のバッファーがある。アップロードの遅延を減らしたいと考えると、160ms のバッファーにデータが溜まらないようにしたいわけです。どのようにすれば良いか。その手前に、もっと細いボトルネックを作ってやればいい。そうすると、一番細いところの直前に溜まるので、ブロードバンドルーターの直前に溜まるようになります。このバッファーサイズをうまい具合に調整してやればいいのです。

バッファーサイズを調整する

バッファーの設計には 3 つのポイントがあります。

第一の要素がバッファーの深さです。帯域を優先したいか低遅延を優先したいかで考え方が多少変わります。基本的には帯域を使いたい場合は無通信時の遅延と同じだけのサイズのバッファーが必要と考えられています。たとえば無通信時の RTT が 100ms だったらバッファーも 100ms 必要です。理由は、パケロスを検知した時に、Reno は RTT の半分の時間黙るからです。つまりバッファーに合計 200ms の遅延があるとしたらパケロスを検知した瞬間に 100ms 黙る。その間、バッファーに溜まっていた 100ms のパケットを流し続けたいので無通信時の RTT と同じだけのサイズのバッファーが必要ということになります。この点 Cubic は黙る時間が短くて 23ms くらい。これが基本的・古典的な考え方です。低遅延を優先する場合、バッファーの深さは無通信時の RTT の 5%で良いと考えられています。どのくらい効率的かというと Reno の場合は 8 割の帯域を使えることになります。2 割は無通信です。100Mbps の回線があって 80Mbps しか使えないんだけれども、RTT がグッと減るからまあいいだろうと。今日主流の Cubic で 9 割使えるのでほぼほぼ問題ないという感じです。

次にバッファーの数です。一般的な通信機ではイーサネットごとに 1 つあります。ブロードバンドルーターの QoS だと、“一般”と“優先”の 2 つに分かれています。さらにインテリジェントなものになると IP アドレスとポートの組ごとにバッファーを作って、順番に投げさせています。一箇所詰まっていても他のところがちょっと投げてきたら先に投げてしまうといったことが通信の組ごとにできるようになっています。

最後に輻輳の通信方法です。「パケットが溜まっちゃってるよ」というのを通知する方法ですが、古典的な方法は、先ほどみていたパケットを落とす方法です。それ以外には最近だと輻輳検知 (ECN) のマークをつけることがあります。「そろそろパケットがいっぱいになってきてるよ」とマークをつけてそのまま流してやるんですね。それが ack とともに送信者に戻ります。この方法のメリットは、パケットを落とすと送信者が再送するまで情報が届かないわけですが、パケットを落とさないので再送する必要がなく、再送による遅れがない点です。

まとめると、バッファーの設計には 3 つのポイントがあって、古典的なものが多くのブロードバンドが使っているやり方でした。これに対し、AQM(Active Queue Management) というのが、0.05RTT の深さであるとか、IP アドレス・ポート毎にバッファーを持ったり、輻輳検知に ECN を使ったりする新しい仕組みです。AQM は最近の Linux で利用することができます。tc、qdisc、 codel、cake などで検索すると情報を見つけることができます。

  古典的 AQM
バッファーの深さ 1RTT 0.05RTT
バッファーの数 1(QoS だと 2) IP アドレス+ポート毎
輻輳通知 パケロス ECN

というわけで、自宅のルーターを Linux に変えて、AQM を設定したわけです。バンド幅を 30Mbps に絞り、35Mbps のボトルネックの手前に持ってきました。これでバッファーが 5Mbps になりました。 しかしこれだけだと集合モデムから卓内モデムの上りの通信でもパケロスが発生していて、マイクラのゲームが遅延してしまうので、Linux ルーターと PC の間を 90Mbps に絞り、ECN を使ってパケロスを回避することにしました。これでネットとゲームが同時に快適になりました。

スプラトゥーンがエラーに

今度はスプラトゥーンがエラーになってみんなで遊べないという問題が起きました。なぜか。Nintendo Switch は P2P 通信を行います。IPv4 しか対応していません。そのためマルチプレイゲームは NAT 越えをします。Nintendo Switch の NAT 判定は、A ・ B は良い、C ・ D は悪い、F は論外みたいな感じです。ブロードバンドルーターの時は B でしたが、Linux に変えたら D になりました。

NAT ってそもそも何なのか。家のネットワークがあります。IP アドレスは 192.168.0.x です。インターネット通信をやるときに中の IP アドレス (192.168.0.1〜192.168.0.5) には外から直接届かないので、外に届くような IP アドレス (たとえば、x.y.z.a:48923) にルーターで変換してやる。それが NAT です。実は NAT には 2 種類あります。1 つは Cone NAT で、外部から x.y.z.a:48923 にパケットを送ると、どのパソコン、どのサーバーから送っても同じパソコンに届きますよというものです。もう 1 つは Symmetric NAT で、インターネット上の特定のものからしか届かないというものです。

Symmetric NAT にもメリットがあり、ポート枯渇がしにくいんですね。特定の相手としか話さないので、同じポート番号を別の相手に使いまわせるという特性があり、TCP では一般的に使われます。ただ、UDP の場合、たとえば Nintendo Switch でされているような P2P 通信は Cone NAT が望ましい。使っていたブロードバンドルーターは Cone 寄りの何かだったんですが、 Linux NAT に変えた結果 Symmetric NAT になり、スプラトゥーンを遊ぶのに問題が出てきてしまいました

Linux NAT は MAP-E とも相性が悪いです。MAP-E というのは、1 つの IP アドレスを何人もで共有するためにポート番号をたくさん刻んで顧客に振ってきます。Linux NAT は NAT を 64 個とか立てて、ランダムに選択しながら動かすんですが、それぞれの NAT の中でポートが枯渇する可能性があり、あまり綺麗なやり方ではないという問題もあります。

自作 NAT

遅延・パケロス対策には Linux が必要だが、Linux NAT はゲームには不向き。ではどうすればいいか。NAT を自作すればいいんじゃないかという話になるわけですね。

NAT の作り方には 2 種類あります。TUN/TAP と呼ばれる Linux の API を使う方法。もう 1 つは XDP です。TUN/TAP は、仮想ネットワークのインターフェースで、通常のプログラムで read や write を使ってパケットを読み書きすることができます。主な使用例は VPN です。主要な Unix でサポートされています。XDP は、Linux カーネル内で、パケット処理をプログラムで書くための仕組みです。非常に制限の多い C で書くのですが、使えるのは Linux のみです。その代わり 100Gbps とか出ます。

どちらを使うべきか。今回は 100Mbps 出ればよく、XDP を使う必要がありません。カーネル内のプログラムを書くのは面倒だし、書いたとしても設定や監視ツールもたくさん作らないといけない。ゲームのためにそんなのはやってられない。TUN/TAP をスプリプト言語で叩けば、設定はスクリプトでできるし、監視は HTTP サーバー組み込みでできるかもしれない。TUN/TAP とスプリプト言語でやっつけたい。とはいえ速度は重要です。100Mbps ということは、毎秒 1 万パケットが流れます。双方向なので、毎秒 2 万パケット処理しなければならない。そういう、高速でかきやすい言語といえば…Ruby です!!!!

正直、他の言語も考えはしました。TypeScript とか。でも堅牢なコードを書くために型とか書きたくない。それだったら Rust で書く、XDP で書く、という気分になるわけです。そもそも楽をするためにスクリプト言語を選びたい。

というわけで書きました。 https://github.com/kazuho/rat

去年 (2022 年) の 12 月 25 日から書き始めて、その日のうちに基本的な部分は動きました。3 日間くらいかけて書きました。翌年 1 月 1 日にちょっといじって、本番投入しました。先ほどサーバーログインして見てみたら、1 月 1 日に動かした NAT がまだ動き続けていました。2195 時間くらい CPU を使っていますが、まだ元気に動いています。Ruby3.2.0 で、YJIT を使って動かしています。

構成はファイルが 5 つあって、720 行くらい。最新版は機能を足して 1028 行になっていますが、1 月 1 日時点の 720 行のものがまだ動き続けています。家のネットワークが止まると面倒くさいのでなかなか変えるタイミングがありません。

実装

コードを見ていきましょう。パケットの読み書きはこのようになっています。

// tune を開く
IFF_TUN = 1
IFF_NO_PI = 0x1000
TUNSETIFF = 0x400454ca
tundev = open("/dev/net/tun", "r+")
ifreq = [devname, IFF_TUN | IFF_NO_PI].pack("a" + Socket::IFNAMSIZ.to_s + "s!")
tundev.ioctl(TUNSETIFF, ifreq)

// 受信
bytes = tundev.readpartial(1500)

// 送信
tundev.syswrite(packet.bytes)

/dev/net/tunというファイルを開いて、ioctl で「このネットワークに繋いでください」と言うと繋がります。あとは、Socket#readpartialSocket#syswriteを呼べば、パケットの読み書きができます。

パケットのデコードと変換をどうやるかというと、IP というクラスを作り、@bytesに生のバイト列を入れます。@l3というプロパティに IPv4 ヘッダ、IPv6 ヘッダを表現するための仕組みを入れます。IP アドレスの書き換えはチェックサムの書き換えを入れます。同じく@l4も作って、TCP のポート番号とか TCP ヘッダ、UDP ヘッダ、あるいは ICMP (Ping 等) のポートの読み書きやチェックサムの書き換えをサポートする設計になっています。

NAT のコードを見ていきましょう。これが NAT のパケットを書き換えるためのコードです。

class Nat
  def transform(packet)

    // 使用する NAT テーブルを決定
    case packet.l4
    when TCP
      table = @tcp_table
    when UDP
      table = @udp_table
    when ICMPEcho
      table = @icmp_echo_table
    when ICMPError
      return handle_icmp_error(packet) unless egress?(packet)
    end
    return if table.nil?

class Natの中に、transformというメソッドがあります。これはパケットを受け取とると、まず、使用する NAT テーブルをどれにするか決めるために、packet.l4を見ます。TCP だったら TCP テーブルを使います。UDP だったら UDP テーブルを使います、という感じです。わかりやすいですね。どのテーブルも使わないんだったらパケットを落とすということでリターンしています。

   // テーブルルックアップして、アドレスとポートを書き換え
    if egress?(packet)
      entry = table.lookup_egress(packet)
      return unless entry

      entry.packets_sent += 1
      entry.bytes_sent += packet.bytes.length
      packet.src_addr = @global_addr
      packet.l4.src_port = entry.global_port
    else
      entry = table.lookup_ingress(packet)
      return unless entry

      entry.packets_received += 1
      entry.bytes_received += packet.bytes.length
      packet.dest_addr = entry.local_addr
      packet.l4.dest_port = entry.local_port
    end

    packet
  end

続いて、このパケットが egress、つまり家の中からインターネットへ向かう場合は外向きのテーブルをルックアップして、何もなければパケットを落とします。テーブルからエントリが返ってきたら、パケットのソースアドレスを自分のグローバルアドレスに、ソースポートをグローバルポートに書き換えて送ります。パケットが ingress、つまりインターネットから家の中に入ってくる場合は逆のことをやります。テーブルをルックアップして、アドレスをローカルアドレスに書き換えて、ポートもローカルポートに書き換えてパケットを転送します。

次に、NAT テーブルはこんなふうになっています。

class NATTable
  // NAT テーブルのエントリ
  class Entry
    ...
  end

  def initialize(name)
    @name = name
    @locals = {}  // egress ルックアップ用テーブル
    @remotes = {} // ingress ルックアップ用テーブル
    @anchor = Entry.new
    @anchor.next = @anchor.prev = @anchor // LRU (最終アクセス時順) のリンクリスト
  end

NAT テーブルのエントリを表すclass Entryがあります。NAT テーブル自体はいくつかのハッシュでできています。まず、外向きのパケットをルックアップするためのテーブルである@locals、次に、内向きのパケットをルックアップするためのテーブルである@remotes、それから、古い接続はどんどん捨てていかなければいけないので、LRU を管理するためのリンクリストから成っています。これは正直書いていて苦痛でした。遅いから書けないだけで、書こうと思えば SQL で書けるんです。だったら、schema を書いたら insert やインデックスを使ったルックアップを自動生成してくれるようなモジュールってないんですかねぇ。そんな気になりました。

無いからとりあえず書くわけですが、実際にパケットの宛先を決めるクラスがどんなふうになっているか。外向きのパケットを決めるlookup_egressはこのようになっています。

def lookup_egress(packet)
  entry = @locals[local_key_from_packet(packet)]

  if entry.nil?
    global_port = empty_port(packet.dest_addr, packet.l4.dest_port)
    if global_port.nil?
      // ポート枯渇 (省略)
    end
    entry = Entry.new(packet.src_addr, packet.l4.src_port, global_port, packet.dest_addr packet.l4.dest_port)
    @local[local_key_from_packet(packet)] = entry
    @remotes[...] = entry
  else
    entry.unlink
  end
  entry.link(@anchor)
  entry
end

まずパケットに対応する NAT エントリを検索します。エントリが見つからずnilだったら空いているポートを探してエントリを作って登録します。もし見つかったらそれをそのまま使えば良いです。エントリに関しては一旦アンリンクしてからリンクすることで LRU を更新します。

逆向きの、外から内に対してのパケットは、テーブルルックアップしてあればそのエントリを使えば良いし、なければパケットを落とせば良いという感じです。

def lookup_ingress(packet)
  @remotes[local_key_from_packet(packet)]
end

先ほど Cone NAT と Symmetric NAT という 2 種類の NAT があるという話をしました。この差はクラスの継承で表現しています。NAT テーブルから継承しオーバーライドするメソッドが 3 つあります。外向きのパケットをルックアップするために Cone NAT の場合にはpacket.src_attrpacket.l4.src_portを使います。送信元だけでポートを決める感じです。Symmetric NAT の場合には、送信元・送信先の全部のアドレスとポートを使って決めるようになっています。これが違うので、空のポートを探すための仕組みも違います。Cone NAT の場合、空いているグローバルポートを配列で管理し、空いているのが残っていればそれを提供します。Symmetric NAT の場合には、相手のアドレスとポートが異なれば同じグローバルポートを再利用することができるので、グローバルポートをランダムで選択し、被っていればやり直す、ランダムでループを回すというアルゴリズムになっています。

次に設定と起動のコードです。使用するグローバルアドレスは、ほぼ固定なので直書きしています。ポート番号も直書きしています。Ruby でループを回して生成して、table.global_portsにまとめて突っ込んでいます。

以下が rat のメインのコードです。

Thread.new do
  loop do
    loop do
      packet = tun.read
      next unless packet

      tun.write(packet)
    end
  rescue StandardError => e
    p e.full_message(:highlight => false)
  end
end

スレッドで動かしています。パケットを読んで、パケットが壊れていたりしたら捨てます。次に先ほどのtransformを呼んで、変換に失敗したら捨てます。成功したら、tun にパケットを書き戻す、というコードです。

Ruby で書いているので、WEB の UI があります。

webapp = Proc.new do |env|
  if $webif.nil?
    begin
      $webif = eval(File.open("webif.rb").read).call($nat)
    rescue StandardError, SyntaxError => e
      print e.full_message(:highlight => false)
    end
  end
  $webif.call(env) if $webif
end
Thread.new do
  Rackup::Handler::WEBrick.run(webapp, :Host => '0.0.0.0', :Port => 8080)
end

WEB UI を遅延ロードしてやって、それを実行するためのコードを書いています。別スレッドで WEBrick を使ってこのサーバーを動かしています。UI を遅延ロードしているので、必要に応じて UI のプログラムだけ再ロードして動的に機能を追加できるようになっています。

rat のバグは irb を使って直す

rat のバグがあったらどう直すかというと、irb を使います。どういうことか。rat 自体が irb で動いています。サーバーにログインして、irb でたとえばアイドル時間を 100 秒にすると、100 秒以上アイドル状態だった通信が全部消えました。このように動的に設定を書き換えることができます。Ruby は便利ですね。irb は便利ですね。

Ruby なので、パケットのインスペクション (DPI) も簡単にできます。たとえば正規表現を使って TLS の SNI 拡張というのを拾うコードを書くと、通信相手のホスト名が見えてしまいます。こういったものも簡単に書くことができます。

まとめ

高機能な NAT が実質 3 日で完成して、これまで見てきたような色々なことができました。ちなみに Switch の NAT タイプは無事に A になりました。Ruby の柔軟性は素晴らしいと思います。その後やったこととしては、速度改善や IPv6 対応をしました。ではこれで最強のゲーミングルーターはできたのか? ベンチマークをとってみると、全力ダウンロード中にダウンロードの遅延は 20ms、しかし別通信のダウノンロードは 10ms 台でした。非常に速くなっています。低遅延ができています。しかも設定不要です。ECN を使っているので低パケロスにもなりました。NAT タイプ A になり P2P 通信にも対応できました。まとめてみますと、一般のブロードバンドルーターにはできないことがあった。Linux ルーターも P2P 通信に弱い。Linux と Ruby で全部できる最強のゲーミングルーターを作ることができました。Ruby は素晴らしい。

なぜ Ruby が素晴らしいか振り返ってみると Ruby は型付けや所有権、テストなどの拘束具がないプログラミング言語ですね。拘束具はきっちりしたコードを書くためには便利だけれど、要らない時もあります。それに Ruby は柔軟なプログラミング言語です。eval、 require、 irb 等があるので、設定や周辺ツールを頑張る必要がありません。おかげで問題解決に集中することができます。さらに重要な点として高速なプログラミング言語という点があります。パケットを処理しようとすると高速でないと話になりません。Ruby は速いので、C や C++の処理コードを置き換える時に使えます。NAT が動くんだったらルーティングにも使えるんじゃないのかという話も十分あると思います。今強い分野で使われるのもいいですが、新しい分野でもどんどん使われていってほしいなと思います。なんせ速くて楽だから。実際、書いていてすごく楽しかったです。

これをもって「Ruby30 周年おめでとうございます」というはなむけの言葉としたいと思います。ありがとうございました。

基調講演: D is for Dream, V for Vaporware (文字起こし)

スピーカー
まつもとゆきひろ (一般財団法人 Ruby アソシエーション 理事長)

Ruby 誕生から 30 年経ちました。正確には昨日をもって“ Ruby ”という名前をつけてから 30 年経ちました。いつも言っていることですが、ソフトウェアというのは物理的実体がないので、名前をつけた日が ( Ruby という) 概念が生まれた日だと思っていて、名前によって“ Ruby ”というものが発生したので、名前をつけた日を誕生日にしようというこだわりがあります。Wikipedia だと最初にリリースした日が記載されていて、Ruby だと 1995 年 12 月 21 日ということになっていますが、Ruby コミュニティーの中では「名前が重要だ」ということも併せて 2 月 24 日を Ruby の誕生日としていますし、昨日でそれから 30 年経ったので 30 周年ということで皆さんに集まっていただきました。

なぜ Ruby が生まれたか

1993 年 2 月に Ruby を作った最大の理由は、暇だったからです。当時、バブル経済が崩壊して、私が関わったプロジェクトがキャンセルになりました。今まで作ったソフトウェアをメンテする仕事もいただいたんですが、あまりやることがないんですね。たまに使っている人から電話が来るんですが、そうしたら「立ち上げ直してください」とか、場合によっては「PC の電源入れ直してください」で、以上終わりという感じだったので。マネージャーは、他のお金を稼ぐチームのマネージャーも兼任していたので、こっちを何もみてないわけです。そうすると、ほっとけばプログラミングする人間を、机からデスクトップコンピュータを奪わないで暇を持て余した状態にしておくと何が起きるか、というと、プログラムを書くわけです。そういう感じで不景気のおかげで生まれたのが Ruby だったんです。

では、なぜ暇してて作ったソフトウェアが言語だったかという話をしなくてはいけません。 これよりしばらく前なんですが、存在しなかった本として『作りながら学ぶオブジェクト指向』という本が書かれるはずだったんですよ。当時同僚だったら石塚圭樹さんが、最初の Ruby の本の共著者になっていただいた方なんですが、アスキー出版でバイトをしていて、アスキーから『オブジェクト指向プログラミング』という本を出した人でもあったので、次の本として先ほどの『作りながら学ぶオブジェクト指向』はどうだろうかという企画があがったんですね。どういう本かというと、オブジェクト指向プログラミング言語を実際に作ってみてオブジェクト指向とは何ぞやを学ぶ本だったんですね。オブジェクト指向を、言語処理系を作ることによって学ぶということで、のちに「オブジェクト指向とは何ぞや」という無益な論争が起きたわけですけれども、この本が世に出ていたらこのような論争はあまりなかったんじゃないかなと思うような素晴らしい本だったんです。実際にプログラミング言語を作りながら学ぶので、作らないといけないわけですが、当時、先ほど言った社内ツールのために C 言語向けのオブジェクトライブラリを作っていたんですね。当時の名前を「CX ライブラリ」と言います。CX ライブラリのソースコードはテープが劣化して失われてしまったんですけれども、今でも、たとえば Ruby のメソッドキャッシュの部分はここから流用されていたんです。そういう期待の企画ではあったんですが、ちょっと尖りすぎていて一般の人にはウケないから売れないだろうという判断でボツになりました。

しかし、企画はボツになっても、動き始めたものは止まらないので作り続けたわけですね。作り続けたものが、名前を決めるという話になって、Ruby という名前になって、そのまま作り続けて今に至ったわけです。残りの 30 年については今日他の人から話があったのであまり触れませんが、なぜ作り続けたのかっていうと、それは、私の「夢」だったんですね。何かというと、自分の言語を作りたい。ふつう、言語というのは、既存のものがあって、それを「ああ、こんな言語だな」と学ぶわけですよね。そうではなくて、自分が文法を決めて、自分が実装して、自分のコントロールできる言語が欲しいと思っていたんですね。実は Ruby の前に、作りかけの「Tish」という名前の言語がありました。1992 年ごろに、オブジェクト指向シェルっぽいものを作りたいと思って、それが Ruby の原点になりました。「Tish」というのは本邦初公開です。シェルというのはスクリプト言語をイメージしていたので、だいぶ Ruby に近い感じです。且つ、オブジェクト指向機能をもっている。先ほど言った CX ライブラリとともに Ruby の基礎になったんですけれども、名前重要というのがよくわかりますね。もしそのまま「Tish」という名前で公開したとしたらみんな Tish 使いたくないですよね。Ruby だから使いたいんですよね。名前重要だというのが本当によくわかります。

言語を作るという「夢」

その前の 1989 年頃、私は大学生だったんですけれども、卒業論文で「Classic」というプログラミング言語を作りました。これも、恥ずかしいのでたくさんは公開してないんですが。だいたい、研究室というのは研究室のテーマがあってそれを分割して卒論を書くという感じだったんですけれども、私は当時から夢でどうしても自分のプログラミング言語を作りたかったので、先生の言うことをあまり聞かずに、自分の言語をデザインして作って卒業論文を書いて提出したんですね。そうすると卒論発表会の時に、自分の担当の研究室の先生が手を挙げて「これは一体どういうことかね」と質問されてだいぶ厳しい思いをしました。

当時読んでいた本で『Object Oriented Software Construction』があります。Eiffel というプログラミング言語を作った Bertrand Meyer さんが書いた本だったのですけれども、1998 年くらいにこの本を買ってものすごく感銘を受けました。Ada という ALGOL 系のプログラミング言語にオブジェクト指向的拡張を加えて色々な不要なものを落として作られたのが Eiffel という言語でした。Bertrand Meyer さんはフランス人なので、エッフェル塔を作ったさん Gustave Eiffel を尊敬して、その名前をつけました。これと同じベクトルの変化を C に対して加えると、C++ではないオブジェクト指向 C を作れるのではないかというのが私の卒論の原理だったんですね。Eiffel 的観点を入れたので、静的型で多重継承があって、profile 型があるような言語をデザインしました。profile 型というのは同じクラスの一部の仕様を見ることができるという、Go で言うと interface{}型にあたるようなものです。

とりあえず動くところまで持っていって卒業論文を書いたのですが、結果的には Ruby にとって反面教師になりました。結論として Ruby には静的型がありませんでしたし、多重継承も入りませんでしたし、静的型もないので profile 型もなかったということになります。結局作ってわかったのは、Ada や Eiffel のアプローチよりも Lisp や Smalltalk のような柔軟な、フレキシビリティの高いものの方が自分に合っていると思ったんですね。ただ、何らかの形で多重継承は必要だろうと思って Mixin を Ruby に導入しました。動的型や型推論のアイディアもありましたが、Classic もテープの劣化により失われました。ソースコードはどこにもありません。ただ、紙に印刷した卒論は残っています。

さらに遡って 1982 年頃、高校生だったんですけれども、その頃に作っていたのが「Alpha」です。これも本邦初公開です。Alpha は「ぼくのかんがえたさいきょうのげんご」という感じだったんですけれども、当時使っていたコンピュータは PC-8801 (無印) で、同時に使っていたコンピュータが SHARP の PC-1210 というポケットコンピュータでした。どちらも電源を入れると BASIC が動くんですよね。言語を作るとかいう感じではないんですよ。当時、尖ったプログラマたちはアセンブラを駆使して PC-8080 とかで言語を作るみたいな人もいましたけれど、私はそんなとんがったプログラマではないので、当時のプログラミング環境や当時の私のスキルではプログラミング言語を作りたいと思っても作ることが全然できなかったんですね

本屋に行ってプログラミング言語を作る方法はないか調べてみても、『コンパイラ』という本が売っていて開けてみると訳がわからないんですね。中田先生というのちに私の大学の先生になった人が書いた本を読んだんですけれども、大学の教科書はわからないんですよね。もう 1 つ『やさしいコンパイラの作り方』という本もあって、ちょっと薄いし「これならわかるかな」と思ったら、“やさしい”というのがコンパイラの方にかかるんですよ。つまり、仕様が小さいコンパイラ、という。作り方は決してやさしくない

当時インターネットもなくて、しょうがないので、「ちゃんとしたコンピュータが手に入って、ちゃんとしたスキルが身について、ちゃんとプログラミングができるようになったら、こんなプログラミング言語を作りたい」という妄想をするわけですよね。だいたい私は小学生の頃から妄想タイプだったので、「いつか自分の言語ができたらな、へへへ」とか言いながらノートに自分の妄想した言語でプログラムを書いていました。この Alpha については仕様とか何も覚えていなくて、作ったことしか覚えていませんが、ノートの紛失によって失われてしまいました。

さらに遡って 1980 年頃。Samuel R. Delany という人が書いた『バベル - 17』という小説があります。バベル - 17 というのは一種の暗号なんですけれど、その暗号というのは実は人工的な言語であったと、サピア・ウォーフ仮説を反映したような小説で、ぜひ読んでいただきたいです。この時には、人工の言語というものにはまったんですね。たとえばエスペラントであるとか。その後にプログラミング言語っていうものにはまったんですね。というふうに、言語を作ることそのものが私にとって「夢」だったんですね。自分で決めたんですよ。

さっきも言いましたが、プログラミング言語ってふつうの人にとっては学ぶものなんですよね。どこかにあって、それをダウンロードしてあるいは買ってきて、プログラムを書いて実行する。私にとってはプログラミング言語というのは作りたいものなんですね。

40 年前の鳥取・島根を思い出していただけるとイメージできる方ももしかしたらいらっしゃるかもしれませんが、高校生くらいまで、コンピュータとか一般的じゃなかったんですね。秋葉原みたいなところがあるわけでもないし。そうすると、周りにプログラミングする人がいないわけですよ。情報というのは、たとえば『ASCII』とか『I/O』とか『月刊マイコン』とか『RAM』みたいなマイコン雑誌と言われてる雑誌くらいしかなくて、その雑誌には「プログラミング言語新しいのができました」とか「こんな新しいプログラミング言語」みたいな記事が載るわけですね。プログラミング言語に興味ある人のうちの、10 人にひとり〜3 人にひとりくらいはプログラミング言語を作りたいと思うんじゃないだろうかと思っていました。大学に行って周りにコンピュータサイエンスを勉強したい人が増えて、プログラミングに詳しい人が増えると、プログラミング言語を作りたい人は本当に少数派だと気づいたんですけれど、手遅れだったんですね。

こうして、作りたいと思ってから 10 年以上経ってからスキルと環境が追いついて作ることができるようになりました。Ruby というのは今から数えて 40 年以上前から私の持っていた夢が実現したものであると言えます。“ One Man ’ s Dream ”というディズニーの曲がありますが、一人の男が「作りたい!」と思ったものが現実化して、高橋さんが言ったようないろんな段階を踏んで世界に広まり、世界を変えたということが言える。昨日あたりから #ruby30th をみると、本当にたくさんの人たちが Ruby を使って生活が変わったとか、就職できて結婚できてとか怪しい通販の広告みたいな感じがしましたけれど (笑)、それも含めて本当にたくさんの人たちの人生に影響を与えたと思います。自分のうちの犬の名前を Ruby にした人もいましたし、自分の子どもの名前を Ruby にした人もいましたね。タイトルの「D is for Dream」というのは、Ruby は私の夢の実現であるというのをご紹介しようと思って持ってきました。

Ruby の未来

ここからは Ruby の未来の話をします。Ruby のソースコードのうち、私が書いたものがどれくらい残っているかカウントしました。2 年くらい前にカウントした時点では 1 割くらいでした。それから YJIT とかも増えたりしているので、多分今は 1 割以下、5〜7 %くらいあったらいい方という感じです。つまり、今 Ruby のソースコードをダウンロードすると何十万行もあるんですけれど、私が Ruby を作ったと言いつつも、その中で実際に私が作った Ruby のコードというのはもう 1 割を切っているんですね。どちらかというと、「私の作った Ruby」みたいな感じじゃないんですね。今日 Yugui さんが、1.8 の頃は問題があったと言っていましたが、あの頃はだいたい私のコードだったので、問題があったのは私のコードで、問題が解決したのはみなさんのコードなんですね。外見は同じでも中身は最新になっている。そういう意味で言うと宇宙戦艦ヤマトとか銀河鉄道 999 みたいですね。外見は古めかしい軍艦であるとか、外見は古めかしい SL 機関車であるけど、中身は最新であるっていう感じで、松本零士のような感じの構造を持っているんじゃないかなあという気がします。

この傾向はこれからも継続するつもりです。つまり、次々起きてくる問題、現代ならではの問題を解決するんだけども、外観はさほど変わらないと。0.49 から今でも動く Quine が書けるっていう。よく書けましたね、あれ。これからの Ruby というのは、言語仕様はだいたい同じなんだけれども、、文法を少しずつ拡張するかもしれないし、ライブラリも拡張するかもしれないし、既存のライブラリにメソッドが少しずつ増えるとか、そういうことはあるかもしれないし、Ruby を取り巻く文化というのだんだん変化していくかもしれないと思っています。Ruby を取り巻く、あるいは Rails を取り巻く文化さえもどんどん変化していくので、それに伴って Ruby のコミュニティが生成していくコードも変化していくというふうに思います。個別のライブラリやフレームワークもどんどん進化していくだろうと思います。

さらに、Ruby3.0 以降、ツールの充実というのを強く強く訴えてきました。それに応えて TypeProf であるとか RBS であるとか、RuboCop もそうですよね、さまざまなツールが充実してきました。あるいは Language Server Protocol の Ruby LSP のようなものも強化されてきて、VS Code を使って TyepScript を書いているときの体験にだいぶ追いた感じで、補完されたりドキュメントがポップアップされたりするようになってきました。

先ほど奥一穂さんが発表で「Ruby は速い」と繰り返してくださって、特に私が正面に立ってメンテナンスしてた頃は「Ruby は遅い」と毎日のように言われていたので、感慨深いものがありました。Ruby の性能も随分向上しました。

これからの Ruby の変化というのは、「着実な変化」というものを続けていくべきだと思ってるんですね。ただ、着実な変化だけだとおもしろくないんですよ。何かチャレンジして、「もしかしたら失敗するかもしれないけどチャレンジする。成功したら儲け物」みたいなものがコミュニティーに存在しないと、参加していて面白いコミュニティーにならないんじゃないかなと思うんですね。ちょっと前だと YJIT の存在そのものが、「MJIT よりもはるかに速い JIT コンパイラを Ruby に」と聞いて「マジかよ」って思ったんですけど、彼ら Shopify の YJIT チームの人たちはものすごく優秀で、すごいの持ってきてくれて「マジかー」という感じでした

新たなチャレンジについてお話ししようかと思います。まだ 1 行も書いてなく思いついただけですが、それで「V for Vaporware」と。Vaporware とは“存在しないもの”という意味ですが、作りたいものは Static Compiler For Ruby で、最近 AOT (Ahead-of-time) コンパイルと言って、Ruby のプログラムをマシン語に直接変換するという。昔、笹田くんが東大にいた頃の学生さんんとかいろいろチャレンジした人もいるんですけれども、正直いうとあんまり性能が出なかったんですよね。しかし現代においては、使える戦略みたいなものが当時に比べだいぶん増えてきました。Abstract Interpretation であるとか、Profile Guided Type Interface であるとか、Interactive Annotation であるとか、Descriptive Type であるとか。そういうアイディアを使うともうちょっとましなものが作れるんじゃないかなと思っています。

Abstract Interpretation というのは抽象解釈なんですけれども、実行をトレースして型情報を収集するタイプのアルゴリズムです。今、遠藤さんがすごく頑張って作ってくださっている TypeProf の基礎になっているアルゴリズムですけれども、ソースコードから型情報を収集してきて、RBS に似た感じで表現される一種の型データベースを用意すると。

それから、Go1.20 に Profile Guided Optimization というのが入りました。実行時にどんな型で呼ばれたかとか、どういうレシーバに対して呼ばれたかっていうのを保存しておいて、その情報を次回のコンパイルに利用しようというアイディアです。それを最適化だけではなく型推定にも使うために実行時型情報を保存すると。そうすると、抽象解釈で失われがちな型情報を集めることができる、というのが Profile Guided Type Interface です。これは結局 JIT がやっているのと同じアプローチなんですね。JIT というのは実行時の情報を集めてきてそれに合わせてコンパイルするわけなので。そうすると、無駄な型チェックをガードとかで外すことができるので、より高速にコンパイルすることができる余地があると。これも型データベースに保存しておくと型情報が取れるものです。

Interactive Annotation というのは、どちらかというといいアイディアとは思っていないのですが、実行時情報とか抽象解釈だけでは補いきれない情報が存在するので、本当にわからなかったらコンパイラが諦めて聞きにくるんですね。「この行のこの型はこれだと思うんですがあっていますか」とか。ポップアップで聞いてくるという感じです。今までのコンパイルとは全く違うアプローチになるわけですけれども、欠点としては、ソース以外の情報になるので、結局ソースを見ただけではわからない情報をどこかに書いてどこかにしまわれているというのであんまりいいアプローチと思っていません。どうしてもわからない時だけ使う最後の手段じゃないかなと思っています。

Descriptive Type というのは、ふつう、型情報というのは“ String 型”とか“ Integer 型”とか、あるいは Generic として“ Integer の配列”みたいなのを考えるのですけれども、型情報ってもっと多くのものを持つことができるんじゃないかなあと思うんですね。たとえば a=[1, 2, 3] を呼ぶと「a は配列です。要素の型は integer が 3 つ並んでいます。長さは 3 です。freeze されていません」というふうに、より多くの情報を持つことができると思っています。これらの情報を使って、たとえば frozen なオブジェクトがpopみたいな破壊的操作をするメソッドとともに呼ばれると型エラーで弾くことができるわけですよね。あるいは、破壊的なものだとetype、エレメントのタイプが、3 つから 2 つに変わるということもあり得るわけですね。より多くの情報を型にエンコーディングすることができるというふうに思っています。

a = [1, 2, 3]
a #=> type(Array, etype=[int, int, int], len=3, frozen=false)

そうすると、型情報が複雑且つ長くなるので、明示的型宣言のある言語には向かないんですね。つまり型というのは、持ちたい情報を全部詰め込むと型宣言に書けなくなるんですよ。書きたくなくなるんですよね。そうすると、暗黙的型の言語の方が、将来的には勝てるんじゃないかな (だといいな) と思ってるんですね。さらにいうとコンパイル型によって速度が速くなることを考えると Ruby のある種の機能を、「スピードのためだから」と言いながらサブセット化することができるんじゃないかなと思っています。たとえば、いつまでたっても導入できない frozen string literals とか、一昨年紹介したアイディアでまだ実現していない static barrier1とかを導入することができるのではないかなと思っています。

と言っても課題は多くあります。そもそも 1 行も書いていないのが大きな課題です。それ以外に、型推定はだいたい正しいプログラムに正しい型がつくことを期待しているものですが、ソフトウェアを開発しているとだいたい間違いがあるんですね。間違いがある時に、間違ったソースから間違った型情報を引き出して間違ったところとマッチさせると大惨事が起きる可能性があるんですよ。そういう時にどうやってエラー回復するかっていうのが私もアイディアがまだなくて、これがつまづく理由になるかなと思っています。もしかすると、ただ単にエラーメッセージが出てくるだけではなくて、対話的に間違いを教えてくれる型デバッガーのようなものが必要になるかもしれないなと思いつつ、まだ全然わかりません。今後要検討です。

Ruby のようなコンパイル型言語で Crystal がありますが、アプローチが違うんですね。Crystal は従来の静的な型で型推論できるところはしようという感じで、元々 Ruby と互換だったんだけれども、Crystal のコンパイラそのものも最初 Ruby で書いてあったんだけど、だんだん枝分かれしてだんだん違う言語になっているっていうのが Crystal です。型宣言を入れるつもりは全くなくて、型宣言がないままでコンパイル型言語が作れるんじゃないかなあと思っています。

相変わらず形から入るタイプなので、この言語に名前をつけました。「スピネル」と呼ぼうと思っています。スピネルって宝石なんですけれど、某漫画のキャラクターにルビー・ムーンとスピネル・サンっていう二人組が出てくるので、 Ruby と対になるからスピネルだと。コマンド名は spin にしようとか。まだ 1 行も書いてくなくてこれからなんですけど、随分昔から頭の片隅にこういうふうに思ってはいました。物事をゼロから始めると大変なのでほったらかしていたんですが、抽象解釈の実践としての TypeProf、実行時の型情報を集めて最適化したコードを生成する YJIT とか、Ruby のパーサーってメンテが大変なんですが共通パーサーとしての YARP であるとか、そういう道具が揃ってきたので、これらの知見を流用したり参考にしたりすると、もしかするとスピネルっていうのが単なる夢物語ではないところまでいけるのではないかと考えています。ちなみに、うちの猫もスピネルです。

未来のチャレンジとして、非常に興味深いものがあるんですが、過去の RubyKaigi とか Ruby カンファレンスとかのキーノートで私だいたい、いろんなアイディアを打ち明けるんですけれど、実現率って 7 割くらいなんですよね。3 割くらいは言いっぱなしで終わる。この Static Ruby Compiler は、3 割に入るか 7 割に入るか私にはわからないですけれども、ただ、次の 30 年を考えたときに Ruby という言語が生き残るために「価値」を提供し続けることだけは必要だと思うんですね。夢であるとかワクワクする気持ちとかも「価値」の中に含めて提供する必要があると思うんです。その中に「たのしい Ruby」「たのしいプログラミング」っていうのも含まれていると思うんですね。Ruby がどんどん良くなっていくこと、Ruby と関わっていて新しい技術を生み出していくところを見る、自分自身も新しい技術を生み出していくのに参加していくっていうのが楽しさの一環ではないかなと思います。私たちがこのような気持ちを忘れない限り、Ruby はこの先 10 年 20 年 30 年生き残り続けて、そのうちもう「Ruby は死んだ」って言われなくなるんじゃないかと思っています。

Ruby の未来、ワクワクすることを待っているというか、ワクワクすることを作り出すという決意を新たにしつつ今日のキーノートを終わろうと思います。今日のキーノートは日本 Ruby の会、Ruby アソシエーション、ネットワーク応用通信研究所、最近、井上会長と新しい会社作った OSS-Vision、の提供でお送りしました。どうもありがとうございました。

クロージング (文字起こし)

スピーカー
井上浩 (株式会社ネットワーク応用通信研究所 会長)

昨年、代表を交代して、今はネットワーク応用通信研究所の会長です。代表は前田くんと、裕蔵さんです。去年の 12 月までは、ネットワーク応用通信研究所の代表取締役、Ruby アソシエーションの副理事長、島根県情報産業協会としまね OSS 協議会の会長を務めていました。

こちらはネットワーク応用通信研究所が作った年表 です。Ruby30 周年に重なるところがあるので紹介いたします。ネットワーク応用通信研究所の事業が始まったのが 1997 年の 3 月で、この年の 8 月にまつもとさんも参加いただいています。今の代表の前田くんが 1999 年、裕蔵さんが 2002 年ですね。年表の下の方は高橋さんとかいろんな方が語られていいのかなと思います。色々あって、2022 年の末に代表を交代したと。 nacl-pamphlet.png

こういった区切りを迎えたわけですけれども、区切りっていうのは、交代してみて思ったのですが、これまでの 25 年間が正しかったんじゃないのっていうことを言っていただいた気がしました。Ruby30 周年の区切りは、これまでの Ruby の 30 年は非常に正しい時代だったということを証明した区切りだったんじゃないかなと思います。改めて 30 周年、おめでとうございます。次の 30 年ですけれど、新生 NaCl もみなさんどうぞよろしくお願いします。

先ほどまつもとさんも紹介いただいた OSS-Vision を今年から始めようと考えています。まつもとさんが「僕は、専務取締役 CTO だ」と言われるんで、「それじゃあ私は代表の CEO で」と。あと島根県庁におられた杉原さんも一緒に始めたんですが、「私は CIO がいい」ということで、3 人で進めています

OSS、コミュニティーの文化に根差したプロダクトの開発で非常にうまくいった例に Ruby があると思います。マネジメントの実効性は Ruby で証明されたんじゃないかなと思います。その知見を活かして Ruby だけではなくていろんなプロダクトを応援していきたいなということを目指して、事業を進めていけたらと思っています。今年からは OSS-Vision の代表取締役、ネットワーク応用通信研究所の会長、そのほかは変わらず務めています。

サイトの紹介にも書かせていただきましたが、RubyWorld Conference の Closing を、2009 年の第一回目から 14 回ずっと担当してきました。今年も 11 月 9 ・ 10 日に松江で開催されます。ぜひみなさん来ていただければと思いますし、何より 15 回目、記念の大会となりますので、特に毎年参加されている方はぜひお越しください。

改めて Ruby30 周年おめでとうございます。このイベントは一般財団法人 Ruby アソシエーション、一般社団法人 日本 Ruby の会の主催で開催されています。デザインはいつもお世話になっている Yuna さん、T シャツ寄贈は OSS-Vision でした。スピーカーの皆さん、非常に熱いスピーカーの皆さんで、聞いているみなさんはお腹いっぱいになったんではないでしょうか。それに変わらないくらい熱い話をギュッと凝縮いただいた 11 人の皆さんも LT で発表いただきました。お疲れ様でした。

20 周年・ 25 周年を振り返ってみたいなと思います。20 周年は品川の楽天タワーで開催されました。入場が大変だったということで、多くのスタッフにお手伝良いただいてスムーズに進んだと記憶しています。先ほどもご紹介した石塚圭樹さんも来ていただきました。イベントは、池澤あやかさんが盛り上げていただきまして、この 2 月 23 日がちょうど中村さんの誕生日だったということで池澤さんからケーキのプレゼント。マイクに向かって中村さんが「来てよかった!」と言ったのが今でも脳裏に鮮明に残っております。池澤さんプレゼンターでまつもとさんに花束を渡して無事終わりました。25 周年は、サプライズゲストとしてまつもとさんのお嬢さんに来ていただいてプレゼンターとして花束を渡していただきました。まつもとさんはこの企画に対して、「誰がこんなことを考えたんだ」と憤慨して喜びを表しておりました。

そして今回 30 周年ですけれども、プレゼンターは社長に就任された方です (ここで前田修吾さんからまつもとさんへギフトと花束の贈呈)。 30 周年おめでとうございます。これからも皆さんで盛り上げていきたいと思います。ありがとうございました。

贈呈.jpg

この記事を書いた人の感想

かねこ (@neko314) といいます。

私にとって Ruby と Ruby コミュニティーとの出会いは本当にライフチェンジングなもので、あの時感じた「こんなにたのしい営みがあるのか」と電気が走ったかのような衝撃がずっと忘れられません。それだけでなく、 Ruby との出会いがあったから私は他職種からプログラマになったのですが、もし以前の仕事のままだったらコロナ禍で失業して飢えていてもおかしくなかっただろうとも思っています。これは結果論的な話かもしれませんが、 Ruby は人生を変えてくれただけでなく命まで救ってくれた存在です。

言語を作るのが Matz さんの夢だった話は以前から聞いたことはありましたが、10 代の頃まで遡った話は初めてで、その分今まで以上に夢の力を強く感じました。まつもとさんやみなさんに「この夢を見て、そして実現し、継続してくださっていて、本当にありがとうございます」とこの場を借りてお礼を述べたいです。Ruby30 周年おめでとうございます。これからもよろしくお願いします。

また、この記事はたくさんの方のご協力で出来上がりました。 編集してくださった @hasumikin さん、ありがとうございました。始める前から「記事のチェックしますよ」と言ってくださったおかげで、安心して作業を開始することができました。隅々までチェックしてくださり、読みやすく整った記事にすることができました。 レビューしてくださったたくさんの方、写真提供してくださった方、みなさんも本当にありがとうございました。


  1. RubyConf 2020 で紹介があった。 https://youtu.be/JojpqfaPhjI?t=1938