ErlangDay 24

ErlangのOTPにあなたのモジュールをコミットするには

More than 1 year has passed since last update.


背景

2011年からErlang/OTPの疑似乱数モジュールがあまりにもダメなのでなんとかしようといろいろ活動していたら、OTP 18.0に新しい疑似乱数モジュールrandを組み込んでもらえるという幸運に恵まれた。そして最近さらにこのモジュールにjump functionの機能追加をする作業をした1。この過程で「OTPに自分のモジュールをコミットする」ことについて、いろいろなことがわかってきたので、この記事ではざっと要点をまとめてみようと思う。


本当にそのモジュールはOTPで必要?

そもそも論として本当にそのモジュールはOTPで必要かということをまず最初に考えないといけない。OTPは、今後パッケージマネージャを活用して分割される方向にあるとはいえ、基本的にはErlangとは不可分な形でついてくることが前提になっている。ということは、OTPの変更はErlang/OTPのユーザ全員2に対して影響が及ぶ可能性があるという基本的な認識がまず必要になる。それだけの変更をやる価値があるのか、必要があるのか、緊急性があるのか、ということを考えないといけないだろう。

一度追加したモジュールを削除するのは、極めて困難である。手前味噌で恐縮だが、randモジュールが置き換える予定のrandomモジュールにしても、OTP 18で代替のrandモジュールが追加され、19でdeprecatedつまり非推奨の扱いになり3、20で削除という予定が組まれている。ここ2〜3年のErlang/OTPのリリースサイクルを考えると、メジャーバージョンが一つ上がるのに1年かかるので4、移行に最低でも2年はかかると考えておくべきであろう。

モジュールを書く以外にもOTPに貢献する方法はある。パッチを書いてプルリクエスト(PR)を出すのが最も一般的であろう。ただOTPの開発では、PRを出したからといって、すぐにマージされるとは限らない。レビューされて改善点を指摘されるのが一般的だ。言い換えれば、オープンソースであろうとも、ソフトウェアの品質は落とさないという態度は徹底しているように見える。


モジュールのプロトタイピング

OTPは巨大なので5、いちいち全部ビルドしてテストするのはなかなか大変である。そんなわけで次期randomモジュールの開発6を2015年1月にOTPチームの開発者Dan Gudmundsson(以下dgudとする)に持ちかけられた時は、まず次期randomモジュール(現在のrandモジュール)だけのレポジトリemprngを作って、そこで作業を始めることにした。このやり方が良かったのは、とりあえず手持ちのOTP環境でテストできることで、その上でテストが終わった時点であらためてOTPのmasterブランチに対して差分を取ってPRを出せるようにした。

emprngの開発にあたっては、外部からのAPIをできる限り従前のrandomモジュールと同じにしつつ、複数のアルゴリズムを選べるようにし、かつ疑似乱数の内部状態を混乱なく保存して復帰できるようにすることが要求されたため、開発の作業はどうしても複雑になった。幸いemprngに組み込む予定の各種疑似乱数アルゴリズムはそれぞれ別のレポジトリに分離しておいたため、疑似乱数アルゴリズム自身の検証7はそれぞれ独立したレポジトリの作業として行うことができた。


OTPモジュールのテスト

OTPモジュールのユニットテストはすべてCommon Testで行われる。Common TestのUser's Guideに親しんでおく必要があるだろう。もっとも私が書いたテストケースは、基本dgudが書いてくれたものに、見よう見真似でテストを追加していったので、OTPのソースコードのうちテストケース(test/*_SUITE.erl)を読みながら書いていくしかないのだろうと思っている。Common Testのgroup設定を使えば、並列に実行してよいテストを並列化したり、あるいは逐次実行の前後関係を義務付けられたりするので、Common Testそのものに精通しておいて損はないだろう。


Type specは不可欠

ErlangにはDialyzerがあるので、型のチェックは自動でできる。これを怠ると複雑なバグの原因になったりする。私もjump functionの機能追加をしたときに、Dialyzerによるチェックを怠ったため、この分野の第一人者であるKostis Sagonas先生8にカミナリを落とされた。これはdgudの責任ではなくて、私の責任だったと思っている。もっともKostis先生がさらに凄いのは、この時にErlangがTravis CIでビルドテストをしている中にDialyzerの項目がなかったため、Dialyzerを実行させるためのPRを出して改善させたところにある。


ドキュメンテーションも不可欠

Erlangのユーザーアプリケーションに関するドキュメンテーションツールはEDocHexhexdocsなどさまざまなものがあるが、Erlang/OTPにマージするために必要なのはErl_Docgenである。このErl_Docgenの記法を守らないと、まともなドキュメンテーションは書けない。もっとも、実際には一からは書けないので、doc/src/*.xmlを読んで、書き直してビルドしてみるのが早道だろうと思う9


プルリクエストの出し方

最初の一発目でPRがOKになることはまずなくて、reviewerにapproveされる状態に持っていくまでには、何回かのやり取りがあるのが一般的だろう。その際に注意すべきこととして、最低限以下のことはやっておくべきだろう10


  • PRのメッセージには、このPRで何をするのか、どう変わるのか、何に影響が出るのか、何には影響が出ないのかを書く。

  • 相手の指示に基本的には従う。従えない時は理由を述べて説明し、合意点を探る。

  • 何度も指示に従って書き直していると、どうしてもGitのコミットの数が増えて、どこで何をやっているのかわかりにくくなる。その時はsquashしてくれと指示が出るので、masterブランチに対してrebaseするなら、git rebase -i masterとして、コミット内容を整理するのがよいだろう。


OTPチームのメンテナとのコミュニケーション

OTPのPRには、必ずメンテナが担当者として割り当てられる。その人とのコミュニケーションを過不足なくかつ密に取ることが、作業をうまく進められるかの一つのポイントになる。コミュニケーションはGitHubのissue/PRの上、つまりオープンな形で取られることもあれば、個人宛のメール、つまりオープンにする前の調整の段階でのやり取りもある。このあたりの情報の取り扱いについては、一般的なソフトウェア開発の原則が適用できるだろう。

一つポイントがあるとすれば、OTPチームのメンテナは、基本的にスウェーデンあるいはその近辺の時間で動いている11し、彼等は土日には仕事をしない12。必要な情報を適宜送ることは大事だが、あまり無理に返事を求めようとはしないのが吉である。彼等は非常に多くのモジュールについて時間を割いているため多忙である。

OTPチームのメンテナとのコミュニケーションは当然英語ということになるが、私の知る範囲では、英語母語話者はそれほど多くないのと、基本的に仕事に対して確実かつ慎重に進める人達が多いせいか、英語やコミュニケーションスタイルが問題になって作業が滞ることはほとんどなかったように思う。


モジュール追加についてのコミュニティの支持を得るには

Erlang/OTPに限らないと思うが、どんなに良さそうな提案であっても、ユーザコミュニティからの支持が得られなければ、まず採用されることはない。以下、コミュニティの支持を得るために、この記事で例に上げたrandomモジュールをrandモジュールに変える話について何をやったかについて簡単にまとめておく。


  • 問題改善のために自分で書けるコードはどんどん書いてリリースして使ってもらうようにした。OTPには採用されなくとも、必要な人にコードを使ってもらえれば問題は改善されるし、評判が広まれば問題意識も高まる。randモジュールについていえば、採用した、あるいは採用候補になったアルゴリズムはすべて別のレポジトリとして利用できるようにしている。

  • 改善すべき問題について、影響力のあるカンファレンス13で繰り返し発表した。もちろん、問題だけではなく、どのようにすれば改善できるかということを提案して説明した。

  • 改善すべき問題について、実害が出かねないことを、Proof-of-Concept(PoC)のコードで発表した。randomモジュールについては、Cのコードで疑似乱数列を一周期回すのに8時間もかからないことを実証している

  • 実際に影響力を持ちそうな人達に繰り返して改善すべき問題を説明し、説得を試みた。これは個人対個人の話なのでそう簡単にはいかない。

コミュニティの支持を得てすべてが実行に移って完了するには、かなりの時間がかかる。結局のところ、randomモジュールにまつわる問題に関して最初に提案した時(2011年)から、OTP 20で廃止になるまで(2017年)、6年かかっている勘定になる。


モジュールが追加された後のメンテナンス

OTPに仮に何らかのコードが追加されたとすると、メンテナンスの責任はそのコードの作者にも及ぶことになる。オープンソースなので義務ではないと言ってしまえばそれまでだが、道義的責任を考えれば、何らかのバグが出たら対応するのは必須であろう。

また、新しい機能提案をする場合は、モジュール追加と同じぐらいの労力がかかることを覚悟しておくべきである。OTPチームは拙速な変更を好まないし、Erlang/OTPのユーザはたとえバグフィックスであっても拙速な変更を嫌う。それだけリリースの際には動作が確実で最大限バグが取り除かれていることが要求される。


ライセンスの問題

Erlang/OTPはApache License 2.0に基づいて配布されている。これに従わないモジュールは配布することはできない。具体的には最低限以下の作業が必要である。


  • 作者全員がApache License 2.0で再ライセンスすることに同意すること。

  • 作者の著作権表示は希望すれば可能である。形式は "Copyright (c) 2016 Kenji Rikitake"、つまり年度と氏名だけである。

詳細については、OTPチームの承諾が必要なので、個別交渉になる。


まとめと感想

ErlangのOTPに自分のモジュールをコミットするという、あまりできない体験をしたので、その時にどんな問題が発生してどう解決していったかを簡単にまとめた。

一番大事なのは、OTPチームとの信頼関係を築き、それに応えるだけのコードやドキュメンテーションを書いて仕事をすることである。


注釈





  1. すでにmasterブランチにマージされていて、これはOTP 20で入る予定である。OTP 19.2でも動くので、興味のある人はぜひテストして欲しい。 



  2. 当然ElixirやLFEのユーザなど、OTPを間接的な形で多用しているシステムのユーザにも全員影響が出る。 



  3. コンパイル時に警告が出るようになっている。 



  4. R14, R15, R16の間はそれぞれ2年かかっている。詳細は http://erlang.org/download/ の更新日時などを見るとわかる。 



  5. 2016年12月18日現在、手元のOTPのレポジトリはgitの作業ディレクトリ抜きで800Mバイトぐらいになる。gitの作業ディレクトリは230Mバイトぐらいだ。だから1Gバイトぐらい平気で食ってしまっている。 



  6. 当初はrandomモジュールという名前のまま中身を変更することが検討されていた。 



  7. 疑似乱数アルゴリズムの標準コードはすべてCで書かれているため、リグレッションテストを可能にするために、まず特定の条件でCで計算した値を用意しておき、それらに対してErlangで同様のテストを書いて内容が一致するかどうかの試験を行った。 



  8. Kostis先生は質問を始めると、自分が納得するまで絶対に妥協しないという、とても熱い人である。もっとも彼はそれだけの実績を持っていて、Erlangの各種テスト手法としてPropEr, ConcuError, そしてCutErといったツールの開発とリリースを行っているすごい人である。 



  9. 当然全部英語である。 



  10. 学術論文の査読者の修正依頼に従う手順を考えてもらえば、ほぼ同じであることがわかるだろう。 



  11. 日本(UTC+9)とスウェーデン(UTC+1 or UTC+2)との時差は冬は8時間、スウェーデンの夏時間期間中は7時間である。欧州の夏時間は米国やカナダの夏時間とは異なるので注意が必要。 



  12. コントリビュータにとっては「趣味」あるいは「好きでやっていること」かもしれないが、OTPチームにとってはOTPへのコードマージは仕事である。 



  13. Erlang/OTPならErlang Solutionsが開催しているErlang and Elixir Factory(旧Erlang Factory)やErlang and Elixir User Conference (旧Erlang User Conference)が最も影響力が大きいだろうと思う。学術関連では、ACM Erlang Workshopが代表的なものである。最近ではCode MeshやLambdaConfでもErlangやElixirの影響力は増えてきている。ElixirならElixirConf (USA/EU)が代表的だろう。