ゴランは最終的にジェネリックを取得!それはより良い言語を行っていますか?


当初公開deepu.tech .
Golangは最終的には、数年後、複数の提案の後にそれをドラッグした後、ジェネリックのサポートを追加するために、最終的にバージョン1.18で作りました.さて、シンプルさは、言語の初心者であるときに素晴らしいですが、私の個人的な経験では、特に大きなコードベースで、本当に高速に迷惑になる.

ディップケーサシダランദീപു, தீபு, दीपू )

それで最終的にバージョン1.18でジェネリックを導入する正気の決定をします.最終的に人々は、配列またはマップから基本的にちょうど要素を見つけているそれらのコードベースの少なくとも1/3のように取り除くことができます😂😂😂しかし、彼らはその構文を🤷‍♂️
午前5時52分- 2022年7月21日
数年の間Golangで広範囲に働いたPolyglot開発者として、私はそれに非常に批判的でしたin a previous blog post titled "My reflections on Golang" , 2019年あなたがトリガされる準備ができている場合は、先に行くと、そのポストを読んで戻ってくる.私の最大のグリップの1つはGoはジェネリックを持っていなかったことでした.今ではジェネリックを持っているので、goについての私の意見を再評価することにしました.

GOのジェネリック


まず、Genericsがどのように働いているかを見てみましょう.幸いにも、Goのジェネリックは、C +やJava、C - CHER、Rustのような他の言語と全く同じように動作します.もちろん、そこにいくつかのマイナーな違いといくつかの行方不明の機能は、GOバージョンでは、推測として、より簡単です.ジェネリックのデザインドキュメントはcomparisons with Java, C++, and Rust .
私が見たかったけれども<> 構文は、他のほとんどの人気の言語に沿って維持するには、あるようだpretty good reasons 使う[] 代わりに、チャネルと複数の変数の割り当てと混合するときにあいまいさを避けるように.
いくつかの例を見てみましょう.Go開発者が通常重複したコードを書く1つの一般的な使用ケースは、配列、地図操作、find、filter、map、およびreduceです.私はこのことについて悩んでいることを思い出すことができますLodash 同じ理由で.ジェネリックを使用すると、最終的に再利用可能なユーティリティコードを一度書くことができますし、すべてのデータ型に使用します.
ジェネリックの有無の配列に対してfindindexメソッドを記述しましょう.

ジェネリックなしで


ジェネリックを使用しない場合は、使用するデータの種類ごとに関数を記述し、構造体を使用すると、すべての一意の構造体に対する関数が必要になります.大きなコードベースでは、唯一の違いがメソッドシグネチャの引数の型であるユーティリティをカバーするコードの数千と数千行を見つけるのは一般的です.これは、ドライ(完全に自分自身を繰り返す)原則の反対です.新しいデータ型を使用するたびに、新しい関数またはコードの新しいブロックを記述する必要があります.イクス!
func main() {
    a := FindIndexFromStringArray([]string{"a", "b", "c"}, "a")
    b := FindIndexFromIntArray([]int{1, 2, 3}, 2)
    c := FindIndexFromFloat64Array([]float64{1.1, 2.2, 3.3}, 3.3)
    println(a, b, c)
}

func FindIndexFromStringArray(arr []string, target string) int {
    for i, v := range arr {
        if v == target {
            return i
        }
    }
    return -1
}

func FindIndexFromIntArray(arr []int, target int) int {
    for i, v := range arr {
        if v == target {
            return i
        }
    }
    return -1
}

func FindIndexFromFloat64Array(arr []float64, target float64) int {
    for i, v := range arr {
        if v == target {
            return i
        }
    }
    return -1
}

// and more

ジェネリックで


ジェネリックを持つ1つの関数に上記のコードを簡素化することができます.
func main() {
    type Foo struct {
        a string
        b bool
    }

    a := FindIndexFromArray([]string{"a", "b", "c"}, "a")
    b := FindIndexFromArray([]int{1, 2, 3}, 2)
    c := FindIndexFromArray([]float64{1.1, 2.2, 3.3}, 3.3)
    d := FindIndexFromArray([]Foo{
    {"a", true}, {"b", false}, {"c", true},
  }, Foo{"a", true})
    println(a, b, c, d)
}

func FindIndexFromArray[T comparable](arr []T, target T) int {
    for i, v := range arr {
        if v == target {
            return i
        }
    }
    return -1
}
ジェネリックはコードベースで重複するコードを大幅に削減します.また、マップ、縮小、フィルタなどの他の有用な一般的な関数を書くことができますなどの配列とマップ.ここにいくつかexamples from the official design document .
// Map over any slice using the given mapping function.
func Map[T1, T2 any](s []T1, f func(T1) T2) []T2 {
  r := make([]T2, len(s))
  for i, v := range s {
    r[i] = f(v)
  }
  return r
}

floats := Map([]int{1, 2, 3}, func(i int) float64 { return float64(i) })

// Reduce any slice using the given reduction function.
func Reduce[T1, T2 any](s []T1, initializer T2, f func(T2, T1) T2) T2 {
  r := initializer
  for _, v := range s {
    r = f(r, v)
  }
  return r
}

sum := Reduce([]int{1, 2, 3}, 0, func(i, j int) int { return i + j })

// Filter any slice using the given predicate function.
func Filter[T any](s []T, f func(T) bool) []T {
  var r []T
  for _, v := range s {
    if f(v) {
      r = append(r, v)
    }
  }
  return r
}

evens := Filter([]int{1, 2, 3}, func(i int) bool { return i%2 == 0 })

// Keys returns a slice of keys from a map.
func Keys[K comparable, V any](m map[K]V) []K {
  r := make([]K, 0, len(m))
  for k := range m {
    r = append(r, k)
  }
  return r
}

keys := Keys(map[string]int{"a":2, "b":4})
ジェネリック型を宣言するとき、型は特定できますT comparable ), 任意のT any ), 近似T ~string ), またはユニオンT int64 | float64 | int ). 型制約は、型別名としても定義できます.
JavaやRustのように、関数、構造体コンテナ、インターフェイスの実装などのGoでジェネリックを使用することができます.これは、Boilerplateコードを削減し、はるかに楽しくコードを書くことができます.

これはよく行きますか。


はい!間違いなく、ジェネリックはもっと楽しく書くために行く.ジェネリックは複雑さのビットを追加しますが、IMOはあなたが取り除くことができるボイラー板の量は、複雑さを追加価値がある.JavaやTypescript、Rust、C CHERHER、C++のような言語に使用されているポリグロット開発者にとっては、これは彼らが試みに行くのを妨げるかもしれない優れた機能です.私は自分の中で好きなことについて話しましたprevious post , それで、私はそれをここで繰り返さないつもりです.私が好きでなかったものが同じままであるならば、そして、ジェネリックがそれらの痛み点を軽減するのを助けるならば、見ましょう.これは私の趣味と経験、特にポリグロット開発者として非常に独創的な基盤であることに注意してください.

  • ジェネリック✅ 最後にここで素晴らしい作品

  • エラー処理❌ それはまだ退屈であり、ボイラープレートを必要とする

  • デフォルト値:❌ メソッドのデフォルト値はまだありません

  • あまりにも多くのボイラープレート:✅ ジェネリックの導入は多くを取り除く

  • 依存性管理❌ まだGOの依存関係管理のファンではなく、特にバージョンを処理する方法は扱われます

  • Gopathのソースコード:✅ GOモジュールでもう問題はありません

  • 混乱するポインタ動作❌ ポインタはまだ混乱しており、注意して使用する必要があります

  • struct地獄:✅ ジェネリックは、この痛み点を非常により痛くするのを助けなければなりません

  • 奇妙なインターフェイスの構築:❌ 私はまだこれのファンでありません、そして、IMO

  • 単一のGCアルゴリズム❌ 多分、私はちょうどJavaによって甘やかされます.多くの証言詳細the Go GC algorithm いくつかのユースケースでは動作しません.

  • 開発者経験:✅ 私は、これが長年にわたって改善したと言います.まだ錆と同じくらい良いが、他の多くの言語より良い.
  • どこに行くのですか。


    最近、システムプログラミングとCLISに興味がありました.私がさびを知らなかったならば、私はそれらのために試みを使いました.私が錆を使い始めたら、私は行きましたI didn't see much reason to use it over Rust 私が興味があったユースケースのために.正直なところ、囲碁の迷惑は、私が錆を学ぶように促した理由の一つでした.私は、建物のようないくつかの簡単なもののためにGOを使いましたCLI for the Elgato Keylights , そして、おそらく私がマイクロサービスを構築するユースケースを持っていたなら、Javaと一緒に考えを与えたかもしれません.ジェネリック作りがはるかに良い、IMOに行くと、私はマイクロサービスと簡単なクリスのためのより多くの重みを与えるかもしれません.