洋食の日記

「だ・である」調ではなく「です・ます」調で書きはじめれば良かったなと後悔してる人のブログです

ひどい腱鞘炎になったので型注釈を書いている

はじめに

タイトルのとおり。色々あって仕事でのタイプ量が急増したために、4月末頃からヒドい腱鞘炎に悩まされている。手首から先の曲がる箇所は全て曲げると痛い。朝起きてしばらくは、指が痛みで曲げられないほどで、仕事では、サポーターで手首から先を固めてタイプしている。整形外科でもらった塗り薬を辛抱に塗り続けているが、おそらく無職になって1ヶ月とか完全に休むほうが効果あるんだろうな...

当然ながら、プライベートの開発は、以前と同じペースでできない状態にある。この記事も少しずつ書いた。趣味の格ゲーも禁じ、休日はダラダラと過ごしていたのだが、それだと生活が無気力な感じになってしまい、精神面で良くないと考え、無理のない範囲でできることをやることにした。

RBSは、Rubyコードの型注釈を記述するもので、Ruby3で話題になったが、Gemをインストールすれば2.6や2.7でも利用できる。RBSは、rbsコマンドによりスケルトンコードを自動生成できるので、バリバリタイプする必要はない。そんなわけで、自分が作成しているGemのRBSを、少しずつ書いていくことにした。

RBSとSteepの準備

SteepはRBSをもとに型検査をおこなう。これらをインストールする。

$ gem install rbs steep

Steepの設定ファイルであるSteepfileを用意する。以下であれば、sigディレクトリ以下にあるRBSファイルをもとに、libディレクトリ以下のrbファイルを検査するよ、という意味になる。

$ steep init
$ vim Steepfile
target :lib do
  signature "sig"

  check "lib"
  
  # ...
end

RBSファイルの自動生成

rbsコマンドで、ベースとなるRBSファイルを自動生成する。普通のRubyコードであれば以下で、だいたいいい感じのものができる。

$ rbs prototype rb lib/hoge.rb > sig/hoge.rbs

native extensionsを使っているのであれば、runtimeを使うとよい。例えば、hoge gemのHogeクラスがnative extensionsを利用しているとしたら、以下のようになる。

$ rbs prototype runtime --require 'hoge' Hoge > sig/hoge.rbs

RBSが書かれいてるGemは、まだまだ少ない。依存しているGemのRBSがない場合は、patch.rbsに必要なものを書くとよい。例えば、Rumaleをはじめ、私が公開しているGemの多くが、Numo::NArrayに依存しているが、以下のようにしてpatch.rbsを用意している(実際はGemで使用しているメソッドだけを抜き出した簡易的なものを用意している)。

$ rbs prototype runtime --require 'numo/narray' Numo::NArray > sig/patch.rbs

これでSteepで型検査ができる。

$ steep check
# Type checking files:

............................................................

No type error detected. 🧋

あとは、RBSが型を推定できず、untypedとしたものを、いい感じに補っていくだけで型注釈ができあがる。

おわりに

少しずつ書いてるうちに、Rumale系のGem(Rumale, Rumale::SVM, Rumale::Torch)をのぞいて、型注釈を用意することができた。無理に型注釈をつけようとせず、untypedを許容する心のゆとりを持つことが、コツな気がする。

RumaleのRBSを書くのが、この作業のラスボスになるが、量が多いのと、コードの修正も予想されることから、時間がかかりそうだ。しかし、Pythonであれば、例えばNumpyやThinc(spaCyで利用されている深層学習ライブラリ)などが型注釈に対応しているので、Ruby機械学習ライブラリとしてなんとか対応したい(Typed Ruby Machine Learningというキャッチフレーズだけ思いついている)。

なによりも、健康な体を取り戻すことが大事で...近況報告でした。