Goにジェネリックがないのはなぜですか?


126

免責事項:私はGoで1日しか遊んだことがないので、多くの機会を逃した可能性があります。

Goでジェネリック/テンプレート/ whatsInANameが実際にサポートされていない理由を誰かが知っていますか?つまり、ジェネリックがありますが、それはmapコンパイラーによって提供されますが、Goプログラマーは独自の実装を作成できません。Goをできるだけ直交にすることについてのすべての話で、なぜジェネリック型を使用できるが、新しい型を作成できないのですか?

特に関数型プログラミングに関して言えば、ラムダやクロージャさえありますが、静的型システムにジェネリックが欠けている場合、どうすれば一般的な高次関数を作成できfilter(predicate, list)ますか?わかりました。リンクされたリストなどは、interface{}タイプセーフを犠牲にして実行できます。

SO / Googleでクイック検索を行っても洞察は得られなかったため、ジェネリックがあったとしても、後から考えてGoに追加されるようです。私はThompsonがJavaの人よりもはるかに優れていると信じていますが、なぜジェネリックスを除外するのですか?または、それらは計画されていて、まだ実装されていませんか?


指摘する価値があると思います。インターフェース{}を使用しても型の安全性は犠牲になりません。これは型であり、他の型にアサート(キャストではない)することができますが、これらのアサーションはランタイムチェックを呼び出して型の安全性を維持します。
cthom06

12
interface{}静的型安全性を犠牲にします。ただし、Schemeには通常、静的な型チェックがないため、Schemeについて次の段落に言及する場合、これはやや奇妙な不満です。
poolie 2010年

@poolie:私が心配しているのは、言語内の1つのパラダイムに固執することです。静的型安全XORを使用していないか。

2
ところで、golang.orgで確認できるように、「GO」ではなく「Go」とつづられています。また、大文字と小文字が区別されます。:-)
poolie 2012

回答:


78

この答えはここにあります:http : //golang.org/doc/faq#generics

Goにジェネリック型がないのはなぜですか?

ジェネリックはいつか追加されるかもしれません。一部のプログラマーが理解していることはわかっていますが、私たちは彼らに緊急性を感じません。

ジェネリックは便利ですが、型システムと実行時の複雑さが犠牲になります。我々はそれについて考え続けていますが、複雑さに比例した価値を与えるデザインをまだ見つけていません。一方、Goの組み込みのマップとスライス、および空のインターフェイスを使用してコンテナーを構築する機能(明示的なアンボックス化)を使用すると、多くの場合、ジェネリックが可能にすることをスムーズに実行できないコードを記述できます。

これは未解決の問題です。


14
@amoebe、「空のインターフェース」interface{}は、最も基本的なインターフェースタイプであり、すべてのオブジェクトがそれを提供します。それらを保持するコンテナを作成すると、任意の(非プリミティブ)オブジェクトを受け入れることができます。したがってObjects、Javaで保持するコンテナーに非常に似ています。
poolie 2012

4
@YinWang Genericsは、型推論環境ではそれほど単純ではありません。さらに重要なことには; interface {}は、Cのvoid *ポインターと同等ではありません。C#のSystem.ObjectまたはObjective-Cのid型のほうが類似しています。タイプ情報は保持され、具体的なタイプに「キャスト」(実際にはアサート)できます。詳細はこちら:golang.org/ref/spec#Type_assertions
tbone

2
@tbone C#のSystem.Object(またはJavaのオブジェクト自体)は、本質的に「Cのvoidポインター」が意味するものです(これらの言語ではポインター演算を実行できない部分を無視しています)。それらは静的型情報が失われる場所です。ランタイムエラーが発生するため、キャストはあまり役に立ちません。
Ian

1
@ChristopherPfohl Dのテンプレートは、コンパイル時間のオーバーヘッドがかなり少ないようで、通常はテンプレートを使用して通常よりも多くのコードを生成しません(実際には、状況によってはコードが少なくなる可能性があります)。
キュービック2014年

3
@ChristopherPfohl Javaジェネリックのみにプリミティブ型のボックス化/ボックス化解除の問題があると思いますか?C#の具体化されたジェネリックには問題はありません。
ca9163d9 2014年

32

行く2

https://blog.golang.org/go2draftにジェネリックのドラフトデザインがあります

行く1

ラス・コックス、囲碁ベテランの一つが書いた一般的なジレンマを題したブログ記事彼は尋ねているが、

…遅いプログラマー、遅いコンパイラーと肥大化したバイナリー、あるいは遅い実行時間を望んでいますか?

遅いジェネリックの結果である遅いプログラマー、遅いコンパイラはジェネリックのようなC ++によって引き起こされ、遅い実行時間はJavaが使用するボクシング-アンボクシングアプローチから生じます。

ブログで言及されていない4番目の可能性は、C#ルートを使用することです。C ++のような特殊なコードを生成するが、実行時に必要な場合。私は本当に気に入っていますが、GoはC#とは非常に異なるため、これはおそらくまったく当てはまりません…

キャスト時に一般的なJava 1.4のような一般的なプログラミング手法を使用するとinterface{}、コンパイル時に型の安全性が失われるだけでなく、ボクシングとアンボクシングとまったく同じ問題が発生します(これが私たちが行っているため)。小さな型(intなど)の場合、Goはinterface{}型を最適化して、インターフェイスにキャストされたintのリストが連続したメモリ領域を占有し、通常のintの2倍のスペースしか使用しないようにします。interface{}ただし、からのキャスト中は、ランタイムチェックのオーバーヘッドがあります。リファレンス

汎用的なサポートを追加するすべてのプロジェクト(プロジェクトにはいくつかあり、すべて興味深いもの)は、コンパイル時コード生成のC ++ルートに一様に移行します。


このジレンマの私の解決策は、プログラムのプロファイルを作成し、パフォーマンスが最も重要な部分を「遅いコンパイラと肥大したバイナリ」モードで再コンパイルするオプションを使用して、デフォルトで「遅い実行時間」に移動することです。そのようなものを実際に実装する人々がC ++のルートをとる傾向があるのは残念です。
user7610 2015年

1
格納されている小さな型(つまりint)は[]interface{}、RAMの2倍のメモリとして使用されると述べました[]int。trueであるにもかかわらず、さらに小さい型(バイト)は、最大16倍のRAMを使用し[]byteます
BMiner 2018

C ++のアプローチには実際にはジレンマはありません。プログラマーがテンプレートコードを書くことを選択した場合、そうすることの利点は、遅いコンパイルのコストを圧倒するはずです。そうでなければ、彼はそれを古い方法で行うことができます。
John Z. Li

ジレンマは、どのアプローチを選択するかについてです。C ++のアプローチでジレンマを解決すると、ジレンマは解決されます。
user7610

9

ジェネリックは現在組み込まれていませんが、コードを生成する小さなユーティリティと組み合わせてコメントを使用する、go用のジェネリックの外部実装がいくつかあります。

これがそのような実装の1つです:http : //clipperhouse.github.io/gen/


1

実際、この投稿によると:

多くの人々は、(誤って)Goチームの立場は「Goにはジェネリックは決してないだろう」と結論付けています。反対に、Goをはるかに柔軟で強力なものにするため、およびGoをはるかに複雑にするためのジェネリックの潜在的な可能性を理解しています。ジェネリックを追加する場合は、可能な限り複雑さを追加せずに、柔軟性とパワーを最大限に活用できるようにしたいと考えています。


-1

パラメトリックポリモーフィズム(ジェネリック)Go 2で検討中ですます。

このアプローチは、型パラメーターの制約を表すために使用できるコントラクトの概念を導入します。

contract Addable(a T) {
  a + a // Could be += also
}

このような契約は、このように使用できます。

func Sum(type T Addable)(l []T) (total T) {
  for _, e := range l {
    total += e
  }
  return total
}

これは現段階での提案です。


あなたのfilter(predicate, list)関数は、このようなタイプのパラメータを用いて実施することもできます。

func Filter(type T)(f func(T) bool, l []T) (result []T) {
  for _, e := range l {
    if f(e) {
      result = append(result, e)
    }
  }
  return result
}

この場合、を拘束する必要はありませんT


1
今日この回答を読んでいる場合は、契約がドラフトプロポーザルから削除されていることに注意してください:go.googlesource.com/proposal
+
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.