Go 2、いよいよ始動

Go 2, here we come! by Robert Griesemer

背景

2017年のGopherConで、Russ Coxは講演「The Future of Go」(ブログ記事)で、Goの次の大きなバージョンについての検討を正式に開始しました。私たちはこの将来の言語を非公式に「Go 2」と呼んできましたが、今ではそれがビッグバン的な単一のメジャーリリースとしてではなく、段階的なステップを経て到来することを理解しています。それでも、Go 2はその将来の言語について語るための呼び名として便利なので、今のところはこの呼称を使い続けることにします。

Go 1とGo 2の大きな違いは、誰が設計に影響を与え、どのように意思決定がなされるかという点です。Go 1は外部からの影響が限られた小さなチームによる取り組みでしたが、Go 2ははるかにコミュニティ主導になります。約10年間使われ続けてきたことで、私たちは当初知らなかった言語やライブラリについて多くを学んできましたが、それはGoコミュニティからのフィードバックがあってこそ可能になったことです。

2015年には、言語やライブラリの変更提案という特定の種類のフィードバックを集めるためにプロポーザルプロセスを導入しました。Goチームのシニアメンバーで構成される委員会が、寄せられたプロポーザルを定期的にレビューし、分類し、判断を下してきました。これはかなりうまく機能してきましたが、このプロセスの一環として、後方互換性のないプロポーザルはすべて無視し、単に「Go 2」というラベルを付けるだけにとどめてきました。2017年には、Go 2という大きな見通しを踏まえたより包括的な計画を優先するため、どんなに小さなものであれ、後方互換性のある段階的な言語変更を行うこと自体もやめました。

今こそGo 2のプロポーザルに取りかかるときですが、そのためにはまず計画が必要です。

現状

執筆時点で、「Go 2 proposal」ラベルの付いたオープンなIssueは約120件あります。それぞれが重要なライブラリまたは言語の変更を提案しており、多くの場合、既存のGo 1互換性保証を満たしていません。Ian Lance Taylorと私はこれらのプロポーザルに目を通し、全体像を把握して対応しやすくするためにGo2CleanupNeedsDecisionなどに分類しました。また、関連するプロポーザルを統合し、明らかにGoの範囲外と思われるもの、あるいは対応不能なものはクローズしました。

残ったプロポーザルのアイデアは、Go 2のライブラリと言語に影響を与える可能性が高いでしょう。早い段階で2つの大きなテーマが浮かび上がってきました。より優れたエラー処理のサポートと、ジェネリクスです。この2つの領域についてのドラフト設計は今年のGopherConで公開されましたが、さらなる検討が必要です。

では残りはどうでしょうか。私たちは、今や数百万人のGoプログラマーと膨大な量のGoコードが存在するという事実に制約されており、エコシステムの分裂を避けるためには、それらすべてを置き去りにせず前進させる必要があります。つまり、多くの変更を行うことはできず、行う変更は慎重に選ばなければならないということです。前進するために、私たちはこうした重要な変更候補のための新しいプロポーザル評価プロセスを導入しようとしています。

プロポーザル評価プロセス

プロポーザル評価プロセスの目的は、少数の選ばれたプロポーザルについてフィードバックを集め、最終的な決定を下せるようにすることです。このプロセスは、おおむねリリースサイクルと並行して進み、次のステップで構成されます。

  1. プロポーザルの選定。Goチームは、採用を検討する価値があると思われる少数のGo 2プロポーザルを、最終決定を下すことなく選びます。選定基準の詳細については後述します。

  2. プロポーザルへのフィードバック。Goチームは、選定したプロポーザルの一覧を記したアナウンスを発信します。このアナウンスでは、選ばれたプロポーザルを前進させるという暫定的な意向をコミュニティに説明し、それぞれについてフィードバックを集めます。これにより、コミュニティは提案や懸念を表明する機会を得られます。

  3. 実装。そのフィードバックをもとに、プロポーザルが実装されます。こうした重要な言語とライブラリの変更は、次のリリースサイクルの初日に提出できる状態にすることを目標とします。

  4. 実装へのフィードバック。開発サイクルの間、Goチームとコミュニティは新機能を試し、さらなるフィードバックを集める機会を得ます。

  5. リリース判断。3ヶ月の開発サイクルの終わり(リリース前の3ヶ月間のリポジトリフリーズが始まる時点)に、リリースサイクル中に得られた経験とフィードバックをもとに、Goチームは各変更を出荷するかどうかの最終決定を下します。これは、その変更が期待した効果をもたらしたか、あるいは予期しないコストを生んでいないかを検討する機会となります。いったん出荷されれば、その変更は言語とライブラリの一部になります。除外されたプロポーザルは、練り直されることもあれば、完全に却下されることもあります。

2回のフィードバックの機会があることで、このプロセスはプロポーザルを却下する方向に傾いています。これによって機能の肥大化を防ぎ、言語を小さくクリーンに保つ助けになることを期待しています。

オープンなGo 2プロポーザルのすべてに対してこのプロセスを実施することはできません。単純に数が多すぎるからです。そこで選定基準が重要になってきます。

プロポーザル選定基準

プロポーザルは、少なくとも次の条件を満たさなければなりません。

  1. 多くの人にとって重要な課題に対処していること

  2. それ以外の人々への影響が最小限であること

  3. 明確でよく理解された解決策を伴っていること

要件1は、私たちが行う変更ができるだけ多くのGo開発者の役に立つこと(コードをより堅牢に、書きやすく、正しくなりやすくするなど)を保証します。一方、要件2は、プログラムを壊したり他の混乱を引き起こしたりすることで害を受ける開発者を、できるだけ少なくするよう注意を払うことを保証します。経験則としては、ある変更によって害を受ける開発者の少なくとも10倍の開発者を助けることを目指すべきです。実際のGoの利用に影響を与えない変更は、実装コストが大きいのに正味の恩恵がゼロであり、避けるべきです。

要件3がなければ、そのプロポーザルの実装ができません。たとえば、私たちはなんらかの形のジェネリシティが多くの人にとって重要な課題を解決するかもしれないと考えていますが、まだ明確でよく理解された解決策を持っていません。それは構いません。ただ、検討の俎上に載せる前に、そのプロポーザルを練り直す必要があるというだけのことです。

プロポーザル

これは私たちの役に立つはずの良い計画だと感じていますが、これはあくまで出発点に過ぎないことを理解しておくのが重要です。このプロセスを実際に使っていく中で、うまく機能しない部分が見えてくるはずで、必要に応じて改善していきます。肝心なのは、実際に使ってみるまでは、どう改善すればよいのかがわからないという点です。

まず着手するのに無難なのは、少数の後方互換性のある言語プロポーザルです。私たちは長い間言語変更を行ってこなかったので、これによってそのモードに戻ることができます。また、こうした変更では既存コードを壊す心配をする必要がないため、試験気球として最適です。

以上を踏まえて、Go 1.13リリース向けに、次のGo 2プロポーザルの選定を提案します(プロポーザル評価プロセスのステップ1)。

  1. #20706 Unicode TR31に基づく汎用Unicode識別子。非西欧のアルファベットを使うGoプログラマーにとって重要な課題に対処するもので、それ以外の人への影響はほとんど、あるいはまったくないはずです。答えるべき正規化に関する疑問があり、そこではコミュニティのフィードバックが重要になりますが、それさえ解決すれば実装の道筋はよく理解されています。なお、識別子のエクスポートルールはこれによって影響を受けません。

  2. #19308#28493 2進整数リテラルと数値リテラル中の_のサポート。比較的小さな変更ですが、多くのプログラマーの間で非常に人気があるようです。「重要な課題」を解決するという基準にはやや届かないかもしれません(16進数はこれまでうまく機能してきました)が、この点で他の多くの言語とGoを肩を並べさせ、一部のプログラマーの悩みの種を解消します。2進整数リテラルや数値フォーマットに関心のない人への影響は最小限であり、実装もよく理解されています。

  3. #19113 シフト数として符号付き整数を許可する。定数でないシフトのうち推定38%が(不自然な)uintへの変換を必要としています(より詳細な内訳はIssueを参照してください)。このプロポーザルは多くのコードをすっきりさせ、シフト式をインデックス式や組み込み関数caplenとよりよく整合させます。コードに対しておおむね良い影響を与えるでしょう。実装もよく理解されています。

次のステップ

このブログ記事によって、私たちはプロポーザル評価プロセスの第1ステップを実行し、第2ステップを開始しました。上に挙げたIssueについてフィードバックを提供するのは、これからはGoコミュニティであるみなさん次第です。

明確で肯定的なフィードバックが得られたプロポーザルについては、実装(プロセスのステップ3)に進みます。次のリリースサイクルの初日(暫定的に2019年2月1日)に変更を実装済みにしたいため、丸2ヶ月間のフィードバック期間(2018年12月・2019年1月)を確保できるよう、今回は少し早めに実装を開始するかもしれません。

3ヶ月の開発サイクル(2019年2月〜5月)の間、選ばれた機能はtip(開発版)で実装され利用可能になり、誰もがそれらを使って経験を積む機会を得られます。これはフィードバックのもう一つの機会となります(プロセスのステップ4)。

最後に、リポジトリフリーズ(2019年5月1日)の直後に、Goチームは新機能を正式に残す(そしてGo 1互換性保証に含める)か、それとも取りやめるか(プロセスの最終ステップ)についての最終決定を下します。

(リポジトリをフリーズするまさにそのタイミングで機能を取り除く必要が生じる現実的な可能性があるため、実装は、その機能を無効化してもシステムの他の部分を不安定にしないような形にする必要があります。言語変更の場合、これはその機能に関連するすべてのコードを内部フラグで保護することを意味するかもしれません。)

このプロセスに従うのは今回が初めてなので、リポジトリフリーズはこのプロセスを振り返り、必要であれば調整するための良い機会にもなるでしょう。どうなるか見てみましょう。

良い評価を!

By Robert Griesemer