RubyKaigi 2019 に参加してきた
下書きとしてだいぶ長いこと放置されていた記事。ドラフトフォルダを整理したかったのと供養したかったので、ブログとして公開することにした。もはや役に立たないし、なんなら途中で書くの放置しちゃってるけど。
『はじめに』のまえに
⚠️ この記事は、下書きとしてだいぶ長いこと放置されていたものを供養するために公開したものとなります。GitHub 上では _drafts フォルダ内で前からアップロードされていました が、いつまでもドラフトフォルダに放置しておきたくなかったので、ブログ公開用フォルダに移動させました。そのため、記事の情報はだいぶ古く、下書きだったため未完成で、もはや役に立たないということをあらかじめご了承ください。
ちなみに、本当はこの記事の作成日にあわせて日付を調整して過去記事として埋もれさせようとしました(役に立たない情報のため)が、SoftBank 光の記事 を今日の日付で公開したので、それにあわせてこちらも今日の日付で公開しちゃうことにしました。Git のコミット日時(2021 年 8 月 28 日)は Ghost からインポートしたときのものなので本来はもっと前(2019 年執筆)だったはずです。
(てかサムネイル右下のブサイクなマスコットはなんだ……?w)
はじめに
4 月 18 日 (木) 〜 4 月 20 日 (土) の 3 日間、福岡で開催された RubyKaigi 2019 に参加してきました。RubyKaigi 2019 は自分にとってのはじめての RubyKaigi でした。そこで学んだことや興味深いと感じたこと、福岡のこと、パーティのことなどを紹介したいと思います。
ちなみに、福岡に行くのも今回が人生ではじめてでした。後述しますが、食べ物がとても美味しかったです。
なぜ Qiita に書かないのか?
RubyKaigi のセッションは Ruby に関する技術的な内容、それもふだんは聞くことができないようなコアな内容を聞くことができます。ここで聞いた内容は技術的に十分価値のあることで、Qiita に書くことも考えたのですが、以下の 2 つの理由によりブログに書くことにしました。
内容が浅くて技術的に参考にならない可能性がある
自分自身の技術力が不足していて、理解できていないことが多い & 間違って解釈している部分が多い可能性が高いと判断したからです。特に C レベルの実装の話や英語でのセッションは、正直言うとほとんど理解することができませんでした……。そんな状態で Qiita に記事を書いても他の人にとってあまり参考になることが書けないと思ったので、気軽に読んでもらえる個人のブログに書こうと思いました。
ちなみに、この記事では各セッションの内容についてはだいたいこんな感じの話だった、程度のことしか書けないので、セッションの内容をもっと詳しく知りたい方は、しばらくしたら RubyKaigi のオフィシャルホームページ で公開されるであろう動画をご覧ください。
技術以外のことも含まれている
RubyKaigi には、もちろん Ruby の技術的なことを学びに行ったわけですが、それ以外の部分、旅行的な内容も文章に書き残しておきたいと思いました。そうなってくると Qiita に書くのはふさわしくないので、ブログに書くことにしました。
RubyKaigi 0 日目
RubyKaigi が始まる前日に福岡に向かいました。会社から電車で羽田空港まで向かい、そこから飛行機で福岡空港までのフライトでした。17 日の 19 時ごろに空を飛び、21 時ごろに福岡の地に足をつけました。飛行機に乗るのは 5 〜 6 年ぶりですが、あの飛行機が離陸する前の加速とそこから宙に浮く瞬間は、何度体験してもワクワクします笑。
東京から福岡までは同じ部署の人と一緒に行き、一旦ホテルにチェックインして荷物を置いた後、違う部署の弊社の人と合流してごはんを食べに行きました。
1 軒目はもつ鍋おおやまというところに行きました。どれも美味しかったんですが、もつ鍋と辛子明太子が特に美味しかったです。もつ鍋は醤油と味噌を食べたのですが、個人的には味噌のほうが好きです。
馬刺しは記憶の限りでははじめて食べたのですが、生で食べられるものなのか気になって「焼かなくて良いんですか?」と聞いてみたら、馬は他の家畜動物に比べて体温が高くて菌が繁殖しにくいから焼かなくても食べられるということを教えてもらいました。
2 軒目は博多藪というお店でカツ丼を食べました。なんか有名なところらしく、弊社の某人がおすすめしていたのでみんなで行こうということになって一緒に連れて行ってもらいました。味の感想を伝えるのが下手なので小並感しか言えないのですが、美味しかったです。
カツ丼を食べ終えてホテルに戻ってきたときには夜中の 1 時を回っていたのでお風呂に入って寝ました。
RubyKaigi 1 日目
ついにはじまった RubyKaigi の初日。8:30 にホテルにある食堂に行き朝ごはんを食べました。
ホテルは朝食付きでした。福岡に来て築地っていうのがおもしろいですが、美味しかったです。
朝食を食べた後、軽く支度をして会場に向かいました。会場までは歩いていきました。
会場に入り、受付を済ませた後、最初のセッションがあるホールへ向かいました。
The Year of Concurrency
最初は Matz のキーノートでした。Ruby のコンカレンシ (並列性) や型、MJIT、マルチコア、パターンマッチング、今後の Ruby についてのことなどを聞きました。
Ruby のメリット・デメリット
Ruby は、生産的で柔軟で書いていて楽しいという良い面がある一方で、他の言語に比べたときのパフォーマンス (実行速度) の問題、マルチコアを活用できていないといった問題があると話していました。
Twitter がパフォーマンスの観点から Ruby (Rails) をやめて Scala に移行したという話は有名ですが、Twitter が Ruby をやめたから Ruby はダメなのか? というとそういうわけではなく、Twitter が Ruby をやめてから今に至るまで、Ruby はバージョンが上がるごとに少しずつパフォーマンスが改善されてきています。GitHub や Airbnb、Cookpad などの有名な企業で使われていることもあり、改善の可能性はあれど、Ruby は十分に良くなっていると話していました。
パフォーマンスに関しては、どの言語も他に比べてものすごく速いというわけでもないと話していました。ところが Ruby は伝統的に遅い遅いと言われることが多いと言っていました。
テスト
テストの話題になったときに、Matz が「実はテストが嫌いなんだ」と言って会場が笑いで盛り上がりました。テストは DRY じゃない (It’s not DRY)、でもテストに代わるコードの品質を担保する手段が今のところ思いつかないから、仕方なくテストを書いている、みたいなことを言っていました。
型定義フォーマット
次に型の話になりました。他のスクリプト言語では型によるアプローチが導入された話がありました。たとえば PHP では type hinting、Python では type annotation、JS では TypeScript が登場し、動的型付け言語でも型を扱えるようになってきました。
Matz は Ruby で型宣言を入れるというのには反対しています。type annotation が嫌い、DRY じゃないから、と言っていました。テストは妥協しているが、型については妥協したくない、では Ruby ではどうするのか、というところで、型定義フォーマットを用意すると言っていました。Ruby のファイルに直接、型を書くのではなく、型を表現できる別のファイル (拡張子は .rbi だそうです) を用意することで型の恩恵を受けられるようにすると言っていました。
型定義や型チェックができるライブラリとして Sorbet や Steep が紹介されていました。
コンカレンシ
並列性の話では、他の言語と比較した上で、Ruby はどのようにしていくのかについて聞きました。まず、最初から並列性があるものとして、Erlang や Elixir が挙げられていました。しかし、Ruby がこれらの言語と同じようにすることはできないと言っていました。今からすべてのオブジェクトをイミュータブルにはできないからです。
それから、Node.js はシングルスレッドだが、たくさんの処理をまとめてさばくことができるのでコンカレンシの部分を補っているという話がありました。その理由として、Node.js で使われている V8 エンジンが速いからとか、non-blocking I/O が挙げられていました。
他にも、Go の goroutine の話もありました。
他の言語がこれらのアプローチをしている中で、Ruby はどうするのか、ということで、Guild (名前はまだ確定ではないらしく、Isolate とも書かれていました) や Autofiber (こちらも名前が確定してないらしく Whatever とも書かれていました) というのが挙げられていましたが、自分の理解力 (技術力) が足りず、これらが何なのかはよくわかりませんでした。名前が決まっていないので、良いネーミングを募集中とのことでした。それから、Guild という名前はゲーム業界からは反対されているそうです笑。
? 文字列
どういう話のときだったかは忘れてしまいましたが、?a と書くと "a" になるという仕様があるのにはとても驚きました。Emacs から来ているらしいのですが、Matz は入れなければ良かった (後悔している) と言っていました笑。ただ、Ruby 3 がリリースされてもこの仕様はなくならないらしいです。
キーワード引数
キーワード引数の仕様を変更するという話もありました。現状のキーワード引数は混乱するときがあるというのと、型との相性が悪いとかで仕様を変更するそうです。どうやら非互換になる (?) ようですが、仕方なくと言っていました。
numbered block paramater
ブロック引数を受け取る代わりに @1, @2, … という感じで暗黙的に値を参照できるようにする新しい仕様です。すでに trunk には入っていて、2.7.0-dev では使えるようになっています。
1
2
3
4
[1, 2, 3].map do |i,|
i * 2
end
# => [2, 4, 6]
と同じことを
1
2
3
4
[1, 2, 3].map do
@1 * 2
end
# => [2, 4, 6]
のように書くことができるというものです。
個人的には便利な機能だなと思っているのですが、賛否両論あるようで、見にくいとか、@ ってインスタンス変数みたいで紛らわしいなどの意見もあるそうです。最終的にこれと同じ仕様で導入されるかどうかはわからなくて、まだ記号などを考えている段階だそうです。
パターンマッチング
RubyKaigi のちょっと前に trunk にマージされたばかりのできたてホヤホヤの新機能です。case … in というキーワードを使ってパターンマッチングができるようになるものです。
最後
スレッドとかキーワード引数を入れたことに関しては Matz は後悔していると言っていました。先を見据えることができていないと言われればそれまでだが、入れてみて後から入れなきゃよかったみたいなことは少なからず起きてしまうことがあると話していました。
で、入れなきゃよかったなら消してしまえば良いかというとそういうわけでもなく、一度入れてしまった機能は、どんなにマニアックで「こんなん誰も使わんだろ」みたいな機能でも世界の誰かが使っちゃうから消せないみたいなことを言っていて会場から笑いが発生していました。つまり、一度入れてしまったものはなかなか消せないし、一度決めてしまったものはなかなか変えられないということです。
Ruby 3 Progress Report
Cookpad のフルタイムコミッタ 笹田さんと遠藤さんとその他 Ruby コミッタの方々のセッションでした。Ruby 3 の現時点での報告という内容でした。
静的解析を強化するということで、先ほどの Matz のキーノートでもあった型定義フォーマットについての話がありました。それから、Ruby 3.0 は、2.0 に比べて 3 倍速くする、Bundler が RubyGems に統合される (?)、などの話がありました。
rubygems.org のアカウント乗っ取りに注意!
rubygems.org のアカウントが乗っ取られるケースが多発しているらしく、Twitter の Bootstrap gem が乗っ取られて悪意のあるコードが挿入されたらしいです。対策として、2 要素認証をオンにする、パスワードを使いまわししない、脆弱な (単純な) パスワードを設定しないなど、注意を呼びかけていました。
numbered block parameter
Matz のキーノートでもありましたが、こちらでも出てきました。@1 が Twitter で書きづらい (メンションになってしまう) と言っていて会場に笑いが起こりましたが、たしかにそうだなあと思いました笑。
キーワード引数
キーワード引数についての話も出てきて、現状のキーワード引数は完全にバグっているらしいです笑。もちろん基本的な使い方では意図通りに動くのですが、ちょっと特殊な書き方をすると既存のものが動かなくなったりするようです。どういうときにそれが起こるのかというのがスライドで説明されていましたが、メモしきれませんでした。
今後の新バージョンのリリース時期について
2.6.3 は 1 日目当日にはすでにリリースされていました。2.7.0 preview 1 が間もなく登場とのことです。2.7.0 は例年通りクリスマスあたりにリリース予定で、3.0 はできれば 2020 年までリリースしたいね、という話でした。
2.7.0-dev は RubyKaigi より前にインストールしていたのですが、2.7.0-dev と 2.7.0 preview 1 は別のものなんですかね? あまりよくわかってないです 😅
Lunch
会場の外に (疑似?) 屋台が並んでいて、そこで焼きそばっぽいものを食べました。
How to use OpenAPI3 for API developer
説明を忘れていましたが、基本的に RubyKaigi では 4 つのセッションが同じ時刻に開催され、その中から自分が興味があるセッションに参加する、という形式です。先ほどの Matz のキーノートと、笹田さん、遠藤さん他のセッションは、同時刻に 1 つのみだったため全員が同じところに集まっていました。詳しくは RubyKaigi 2019 のスケジュール を見るとわかると思います。
4 つのセッションは、セッションによって英語だったり日本語だったりするのですが、ぼくは英語ができないので、無理して英語のセッションを受けに行って内容が全くわからなかった、だともったいないかなと思ったので、基本的に日本語のセッションを聞きに行っています。
さて、このセッションでは OpenAPI についての話を聞きました。@ota42y さんのセッションです。OpenAPI という名前は聞いたことがあるのですが、実際どんなものかはあまり調べたことがなくよく知らなかったので、気になってこのセッションを選びました。
セッションを聞くまで完全に勘違いしていたのですが、OpenAPI とは REST のようなものと並んだ、新しい API の仕様のようなものだと思いこんでいましたが、そうではなく API を記述するためのフォーマットのことみたいです。machine-readable な API を定義できるようになり、定義と実装がずれないようにする目的で使われるようです。
OpenAPI 3 の構造
OpenAPI 3 の構造は、info、paths、servers、components、security、tags、externalDocs などがあります。security の部分には、認証に JWT を使うのか BASIC 認証を使うのか、API キーを使うのか OAuth 2 を使うのかなどの認証ルールを書くことができるみたいです。ただし、これはただの定義なので、security の項目に OAuth 2 と書いたからと言って OAuth 2 が使えるようになるわけではないと言っていました。
OpenAPI の活用方法
OpenAPI は API のルールを記述するもので、それ自体が何かを実現してくれるわけではないです。たとえばこの API はこういうものをパラメータとして受け取り、こういうものを返す、というバリデーションを記述し、それはコンピュータ側でも解釈可能であるようです。
ではなぜ API にバリデーションが必要かと言えば、人間はよくミスをするものなので、本来 API として提供する機能 (API の定義) と実際の挙動 (API の実装) にずれがないかを保証できるようになるからだそうです。
これを実現するのに Committee という Gem があって、クライアントアプリケーションとラックアプリケーションの間に Committee が入り、API の定義と実装がずれていないかをチェックすることができます。たとえば、もし API のリクエストに必須なパラメータが不足していた場合は Committee 側で弾いてくれるのでラックアプリケーションまで到達することがないとのことです。
そして Committee はリクエストだけではなくてレスポンスに対してもバリデーションができるようです。本来返すべき値とは異なるものを返していた場合はエラーになって、そのミスに気づけるから便利だよね、ということみたいです。本番でエラーになると困るので、エラーにはせずにログだけ出すようにする、というオプションもあるようです。
OpenAPI 2 と OpenAPI 3 の違い
OpenAPI 2 と OpenAPI 3 の違いはいくつかあるようですが、security 項目などの新しいものが増えたのが主な変更点のようです。
OpenAPI によるエンドポイントのバリデーションについて
パトラシア木というものが紹介されていました。木構造なので文章だけで説明するのは難儀なのですが、
1
2
3
4
5
6
7
8
9
root
|
users
|
meals
/ \
me {id}
/ \
lunch lunch
のような木構造があったとき、たとえば /users/meals/42/lunch というエンドポイントは、root -> meals ->{id} -> lunch というように辿ります。もし一番下の葉の部分まで辿ることができなかった場合は、どのルーティングにもマッチしない、定義に含まれていないものということになるので、バリデーションで弾かれるということです。
OpenAPI のツールについて
OpenAPI Generator と Swagger UI について紹介されていました。Swagger UI は OpenAPI 3 に対応しているようです。
Pragmatic Monadic Programing in Ruby
@joker1007 さんのセッションです。Ruby でモナド (っぽいこと?) をやってみるという内容でした。
Ruby には Proc というものがありますが、これは手続きを表したオブジェクトで、言い換えれば関数オブジェクトだと言っていました。そして、モナドはデザインパターンの一つであると言っていました。
このセッションについては、自分が関数型プログラミングやモナドについて全然知識がないためまともにメモできたのはこれくらいしかなくて、そこから先はほとんど理解することができませんでした……。耳に入ってきた言葉をそのまま書くと、「モナドとは flat_map である」ということらしいのですが、知識がないため、あまりピンと来ていません……。
Afternoon Break
ここで休憩の時間に入りました。会場内では各スポンサー企業がブースを出していて、会社の紹介やグッズ配布、アンケート、くじなどをやっていました。
Twitter でツイートするときしか写真を撮らなかったので写真は少ないですが、広い部屋に何十社もブースが並んでいてとても賑わっていました。Money Forward のブースを撮影したのは、「このブースに来ています」とツイートしてくれたら Money Forward の T シャツをプレゼントしますと言われたからです笑。
ブースを回っていて特におもしろいなと思ったのは、3 枚目の写真にもある Ruby のクイズです。!????!:!?! を実行すると何が返ってくるか、という問題です。実行してしまえば答えはすぐにわかるのですが、なぜその実行結果になるのか、というのを頭で考えるのはとても良い勉強になりました。自力では理由がわからず、脳内パースできた弊社の人に解説を聞いて理解しました。ここでその答えを書いてしまうとネタバレみたいになってしまうので書かないことにします。
それから、Cookpad のブースでも Ruby のクイズが出題されていました。書かれている Ruby プログラムにできるだけ少ない文字数で文字を足して “Hello world” が出力されるようにするというものです。こちらは最終日 3 日目に解説があって、自分は聞きそびれてしまったのですが、弊社の他の人の話を聞く限りだとかなり難しかったようです。
A Type-level Ruby Interpreter for Testing and Understanding
Cookpad 遠藤さんのセッションです。Matz のキーノートでもあった型についての内容です。型プロファイラや型チェックの実装についての細かい話を聞くことができました。
型プロファイラ
1
2
3
4
5
def foo(n)
n.to_s
end
foo(42)
というプログラムがあったとき、通常のインタプリタでは foo は 42 を受け取って "42" を返す、という解釈をしますが、型プロファイラでは Integer を受け取って String を返すという解釈をすると言っていました。
型プロファイラはこんな感じになっているようです。
1
Object#foo :: (Integer) -> String
上記はメソッド foo が引数に Integer を受け取り String を返すということを定義しているものです。
正確にメモすることができなかったので、表記はもしかしたら間違っているかもしれません。あくまで型プロファイラはこんな感じだという雰囲気だけ読んでいただければと思います。
型チェックは毎回行われるのか
あるメソッドが呼ばれるたびに型チェックが毎回実行されるのかという疑問がありますが、不要である場合はスキップされるということみたいです。
1
2
3
4
5
6
7
def foo(n)
n.to_s
end
foo(42)
foo(43)
foo(42.0)
まず、foo(42) を実行する際は初回の foo メソッドの呼び出しなので型チェックが実行されます。次に foo(43) を実行する際は、すでに foo(42) を実行した段階で Intege を受け取り String を返すということを知っているので型チェックはスキップされます。最後の foo(42.0) では引数が Float になっていて、Float を受け取り String を返す、というのはまだ知らないので型チェックが実行されるということになります。
再帰呼び出しの場合
フィボナッチ数列を例に紹介されていました。
1
2
3
4
5
6
7
def fib(n)
if n > 1
fib(n - 1) + fib(n - 2)
else
n
end
end
上記のフィボナッチ数列を求めるメソッドの場合、if の中ではどんな型を返すのかがわからないので、型チェックは一旦 pending されます。
次に else を見たときに、メソッド fib は Integer を返すことがわかります。ここでメソッド fib は Integer を受け取り Integer を返すことがわかるので、以下のような型プロファイラが出来上がるということみたいです。
1
Object#fib :: (Integer) -> Integer
型プロファイラの課題
型プロファイラにはまだまだ実装上の問題があると言っていました。何個か紹介されていて全部はメモしきれなかったのですが、未実装 (型の解析が難しかったり不可能だったりするもの) や状態爆発 (pending を繰り返すと考慮すべき状態が倍々に増えていく現象) で解析に時間がかかってしまったりする例もあると話していました。
型プロファイラは Ruby プログラマが直接型を書かなくても (型を意識しなくても) 型の恩恵をある程度受けられるようになるもので、自分で型を明示的に扱いたいといった場合は先ほども出てきた Sorbet や Steep を使ってくださいとのことでした。
RMagick, migrate to ImageMagick 7
@watson1978 さんのセッションです。RMagick を改善したという内容です。
RMagick というのは、画像処理ライブラリ ImageMagick を Ruby で扱えるようにするための Gem です。ぼくも実際に使ったことがあるので馴染み深かったです。
RMagick は 2.16.0 がリリースされてから約 2 年ほど開発が停滞していたようですが、Watson さんが開発に加わることによっていろいろ改善され、最近 3.1.0 がリリースされました。
このセッションでは主に 2.16.0 からの改善点について紹介されていました。
セットアップ問題
ぼくも使ったことがあるのでわかるのですが、RMagick のインストールは躓くことが多いです。その理由と改善について初回されていました。
RMagick は ImageMagick 6 に依存しており、macOS の場合、Homebrew で ImageMagick 6 をインストールすると特殊なパスでインストールされるようです。今までの RMagick ではそのパスを認識することができずエラーになることがあったのですが、それを改善して Homebrew でインストールされた ImageMagick 6 のパスを正しく認識できるようにしたという話でした。
それから、Windows でも同じようにセットアップで問題が起きていたようで、MingW と MSWin 関係の問題を解消したと言っていました。
メモリリーク問題
RMagick ではメモリリークが発生するとよく言われていました。Watson さんは 56 件のメモリリークを解消したそうです。この 56 件のメモリリークは 4 パターンくらいに分類できて、それぞれについて具体的にどのような理由でメモリリークが発生していたのかについて紹介されていました。
まず case 1 としては、メモリを確保するところからメモリを開放するまでの間に例外が発生すると正しくメモリを開放することができず、メモリリークが発生するという問題。
case 2 は、メモリを確保しているのに、開放していない、という単純なミス。
case 3 は、ちょっと聞き取れずメモすることができてなかったです。
case 4 は、ImageMagick 6 側がメモリリークしているというケース。これに関しては ImageMagick 側に issue を出して直してもらったようです。ImageMagick 側のメモリリークの一例としては、メモリを確保する全く同じコードが 2 つ書かれているけど、開放するコードは 1 つしか書かれていなくて、片方は開放されるけどもう片方が開放されていなくてメモリリークが発生するという内容でした。
どうやってメモリリークを特定しているのか
Watson さんは Xcode に入っている profiler tool というのを使っているようです。これはメモリの使用量をリアルタイムで把握することができる機能のようです。
ソースコード中のどの箇所でメモリを大量に消費しているかを観察して、怪しいところがあったら該当コードを読んで解決する、という手順でメモリリークを発見しているそうです。
ただ、profiler tool は便利だが、テストができているものしかカバーできないと言っていました。そして残念ながら RMagick にはテストが少ないらしいので、これから少しずつテストを増やしていく予定だと話していました。そしてテストが増えることでまた新たなメモリリークが発見されるかもしれないと言っていました。
SEGV の修正
メモリリークの他にも SEGV を修正したと言っていました。5 件あるうちの 4 件を修正し、1 件は workaround ということみたいです。
SEGV に関しては詳しいことまでは理解できなかったのですが、ruby_stack_length() というのが 47GB (この数値は間違っているかもしれないです 🙇♂️) という謎の値を示していて、その状態で GC が走ると 47GB 分の GC を実行しようとするのでクラッシュする、と言ったような話だったと思います。詳しいことはよくわからなかったです。
この問題が Ruby 側にあるのか RMagick 側にあるのか ImageMagick 側にあるのかが特定できないらしく、現在は workaround になっているということみたいです。
今後のサポート
RMagick は ImageMagick 6 に依存していますが、ImageMagick 7 をサポートできるようにしたいと話していました。ImageMagick 7 をサポートするにあたっての今後のプランについて、以下の 3 つの方法があると言っていました。
- plan 1: RMagick 3.x で ImageMagick 6 をサポートし、RMagick 4.x で ImageMagick 7 をサポートする
- plan 2: 環境によって ImageMagick 6 を使うか ImageMagick 7 を使うかを
ifで条件分岐する - plan 3: Bundler で分離する
plan 1 は RMagick 4.x でまたインストール失敗が続出しそうだからあまり乗り気ではなさそうな感じでした。plan 2 は条件分岐が増えるので RMagick のコードが煩雑になるから Watson さん的にはあまりやりたくないらしいです。が、他のメンバーは plan 2 に賛成しているみたいです。
最終的にどのようなプランで ImageMagick 7 をサポートするのかはまだ決まっていないようですが、RMagick はぼくも使っている Gem の一つなので、どんどん使いやすくなっていったら嬉しいです。
Pattern matching - New feature in Ruby 2.7
@k_tsj さんのセッションです。Matz のキーノートでも紹介されていたパターンマッチングの話です。Ruby の中でちょうどホットな機能の一つなので興味が出て聞きに行きました。
1
2
3
4
5
6
case [0, [1, 2, 3]]
in [a, [b, *c]]
p a # => 0
p b # => 1
p c # => [2, 3]
end
上記のプログラムのようにパターンマッチを行うことができるようになります。現在 trunk に入っているパターンマッチには case … in というシンタックスを使用するようですが、実験的な機能であるため、今後シンタックスは変更になる可能性があると言っていました。パターンマッチを使用すれば今まで以上にシンプルに書けるのでとても嬉しい機能です。
1
2
3
4
case [0, 1]
in [a, b] unless a == b
:reachable
end
のようにパターンマッチさせる際の条件 (上記の例だと unless a == b) を指定することもできるようです。他の言語では「ガード」と呼ばれていたりするものです。
パターンの種類
以下の数種類のパターンについて紹介されていました。
- value pattern
- variable pattern
- alternative pattern
- as pattern
- array pattern
- hash pattern
value pattern は名前の通り、値でマッチさせるパターンです。
1
2
3
4
5
6
7
8
case val
in 0
p 'val is 0'
in 1
p 'val is 1'
in 2
p 'val is 2'
end
variable pattern も名前の通りで、変数にマッチさせるパターンです。
1
2
3
4
case 1
in a
p a # => 1
end
なお、パターンマッチに使用する変数が他のスコープにあった場合は無視されるようです。
1
2
3
4
5
6
a = 0
case 1
in a
p a # => 1
end
ただし、変数の前にキャレット (^) をつけると、参照してくれるようになります。
1
2
3
4
5
6
7
a = 0
case 1
in ^a
:unreachable
end
# => NoMatchingPatternError
上記のプログラムの in ^a の部分は in 0 となるのでマッチせず NoMatchingPatternError となるようです。
alternative pattern というのは、| を使っていずれかにマッチさせるパターンです。
1
2
3
4
case 0
in 0 | 1 | 2
:reachable
end
as pattern は => という記号を使って、具体的な値ではなく型がマッチしているかどうかのようにより広い範囲でマッチさせることができます。聞き間違いでなければ、case … when と同じように === で比較するようです。
1
2
3
4
case 0
in Integer => a
p a # => 0
end
array pattern と hash pattern はそれぞれ Array、Hash を使ってパターンマッチする方法です。hash pattern の例でいうと以下のように書くことができるようです。
1
2
3
4
5
case { a: 0, b: 1 }
in { a:, b: }
p a # => 0
p b # => 1
end
このセッションでは、特に array pattern と hash pattern が重要だと説明がありましたが、ぼくがあまりうまく説明できる自信がないので、詳しくは他の記事や後日公開予定の動画をご覧いただけるとありがたいです。
まとめ
今回のパターンマッチを導入するにあたり、互換性を重視したと言っていました。最初は case … in ではなく match という予約語を新たに追加するという案もあったようですが、それだとすでに match という変数を使用しているプログラムが動かなくなってしまうので、新しい予約語は入れないという方針にしたようです。case … when に似た構文で、代わりに使えるキーワードは何か、と考えたときに case … in というのが出てきたようです。
それから、パターンマッチはそもそも関数型プログラミングの世界のものなので、関数型プログラミングの言語の記法をそもまま持ってきても Ruby らしくないと話していました。なので、Ruby の強力な Array、Hash のサポートを活用して実装したと言っていました。
Party, Party, Party!
最後のセッションが終わった後、パーティがありました。中洲商店街を使って立ち食い、立ち呑みをしました。貸し切りという感じではなかったですが、商店街の各ヶ所に料理が並んでいたりお店の人が料理を提供してくれたりして、それを食べながら他の Rubyist の方々と盛り上がるというイベントでした。
ここで他の Rubyist と交流を深めるチャンスだったと思うのですが、ただでさえコミュ障だというのに、それに拍車をかけて開けた空間で会話の輪に入り込むというのは自分にとってはかなり難易度が高く、なかなか会話することができませんでした…… 😥
パーティが終わった後は弊社の人からラーメンを食べに行こうと誘っていただきましたが、体力の限界が近づいていたのでそのままホテルに返ってシャワーを浴びて寝ました。本当はラーメン食べたかった! けど、無理すると翌日がつらいので我慢しました。
RubyKaigi 2 日目
2 日目の朝がやってきました。前日は早めに寝たのでよく眠れました。
2 日目の朝食は魚の定食を食べました。美味しかったです (小並感)。
All bugfixes are incompatibilities
@nagachika さんのキーノートです。最新版ではなく、安定版の Ruby をメンテナンスしている nagachika さんが、安定版をメンテナンスするときに注意すべきことなどについて発表していました。
謝辞
今回の RubyKaigi 2019 は会社の経費として参加しました。4 月は期の代わりということもあって経費が降ろすのが困難だったようです。その状況でこの RubyKaigi に参加するための交通費や旅費を工面していただいた会社 (部署) には非常に感謝しております。ありがとうございます 🙏🙏🙏
交通費や旅費、参加費などをすべて含めるとかなりの出費になる、というのもそうなのですが、今月はかなりお金に困っている状況だった (この話はここでは省略します笑) ので、会社の経費がなければ参加することができませんでした。
それから、弊社の社員の方々にも、美味しいごはん屋さんに連れて行ってもらいごはんを奢っていただいたり、RubyKaigi のミートアップやパーティについての情報を教えていただたり、様々な面で助けていただきました。はじめての RubyKaigi ということでわからないことも多かったので非常に助かりました。ありがとうございます 🙏🙏🙏
今回の RubyKaigi で今まで話す機会がなかった弊社の Rubyist の人たちとも仲良くなれた (?) ので良かったです!
のらは ちからつきた!
『謝辞』できれいにシマッているように見えますが、『RubyKaigi 2 日目』がほとんど書かれていません。『All bugfixes are incompatibilities』を書き始めたところでちからつきたようです。それに、RubyKaigi は 3 日間だったはずなので、3 日目についてはまったく記録が残っていないですね。あまりにも詳細に書こうとしすぎて途中でめんどくさくなってしまったようです……。来世に期待しましょう。
















