Mengapa tidak ada obat generik di Go?

126

Penafian: Saya hanya bermain dengan Go untuk satu hari sekarang, jadi ada kemungkinan besar saya telah melewatkan banyak hal.

Adakah yang tahu mengapa tidak ada dukungan nyata untuk generik / templates / whatsInAName di Go? Jadi ada generik map, tapi itu disediakan oleh compiler, sementara programmer Go tidak bisa menulis implementasinya sendiri. Dengan semua pembicaraan tentang membuat Go se-ortogonal mungkin, mengapa saya dapat MENGGUNAKAN tipe generik tetapi tidak MEMBUAT yang baru?

Terutama ketika datang ke pemrograman fungsional, ada lambda, bahkan closure, tetapi dengan sistem tipe statis yang tidak memiliki generik, bagaimana cara saya menulis, yah, fungsi generik tingkat tinggi filter(predicate, list)? OK, daftar Linked dan sejenisnya bisa dilakukan dengan interface{}mengorbankan keamanan tipe.

Karena pencarian cepat di SO / Google tidak mengungkapkan wawasan apa pun, sepertinya obat generik, jika ada, akan ditambahkan ke Go sebagai renungan. Saya percaya Thompson melakukan cara yang lebih baik daripada orang-orang Java, tetapi mengapa tidak menggunakan obat generik? Atau apakah sudah direncanakan dan belum diterapkan?

s4y
sumber
Saya pikir ada gunanya menunjukkan: menggunakan antarmuka {} tidak mengorbankan keamanan tipe. Ini adalah tipe, dan dapat ditegaskan (bukan ditransmisikan) ke tipe lain, tetapi pernyataan ini masih meminta pemeriksaan waktu proses untuk menjaga keamanan tipe.
cthom06
12
interface{}mengorbankan keamanan tipe statis . Namun ini adalah keluhan yang agak aneh untuk dibuat ketika menyebutkan Skema adalah paragraf berikutnya, karena Skema biasanya tidak memiliki pemeriksaan tipe statis.
poolie
@poolie: Yang saya khawatirkan adalah berpegang teguh pada SATU paradigma dalam sebuah bahasa. Entah saya menggunakan keamanan tipe statis XOR tidak.
2
btw dieja 'Go', bukan 'GO', seperti yang bisa kamu lihat di golang.org. Dan peka huruf besar / kecil. :-)
poolie
1
bagaimana dengan github.com/facebookgo/generics ?
Thellimist

Jawaban:

78

jawaban ini Anda akan temukan di sini: http://golang.org/doc/faq#generics

Mengapa Go tidak memiliki tipe generik?

Generik mungkin akan ditambahkan di beberapa titik. Kami tidak merasakan urgensi untuk mereka, meskipun kami memahami beberapa programmer melakukannya.

Obat generik memang nyaman tetapi harganya mahal dalam kompleksitas sistem tipe dan waktu pengoperasian. Kami belum menemukan desain yang memberikan nilai yang sebanding dengan kompleksitasnya, meskipun kami terus memikirkannya. Sementara itu, peta dan irisan bawaan Go, ditambah kemampuan untuk menggunakan antarmuka kosong untuk membuat wadah (dengan unboxing eksplisit) berarti dalam banyak kasus dimungkinkan untuk menulis kode yang melakukan apa yang akan diaktifkan oleh generik, jika kurang lancar.

Ini tetap menjadi masalah terbuka.

Vinzenz
sumber
14
@amoebe, "antarmuka kosong", dieja interface{}, adalah tipe antarmuka paling dasar, dan setiap objek menyediakannya. Jika Anda membuat wadah yang menampungnya, ia dapat menerima objek (non-primitif) apa pun. Jadi sangat mirip dengan wadah penampung Objectsdi Jawa.
Poolie
4
@YinWang Generik tidak sesederhana itu dalam lingkungan tipe kesimpulan. Lebih penting; interface {} tidak setara dengan void * pointer di C. Analogi yang lebih baik adalah tipe id System.Object atau Objective-C C #. Informasi jenis dipertahankan dan dapat "dilemparkan" (ditegaskan, sebenarnya) kembali ke jenis konkretnya. Dapatkan detail detailnya di sini: golang.org/ref/spec#Type_assertions
tbone
2
@ Tbone C #'s System.Object (atau Object Java per se) pada dasarnya adalah apa yang saya maksud dengan "C's void pointer" (mengabaikan bagian yang tidak dapat Anda lakukan pointer aritmatika dalam bahasa tersebut). Di situlah informasi tipe statis hilang. Sebuah cast tidak akan banyak membantu karena Anda akan mendapatkan error runtime.
Ian
1
Template @ChristopherPfohl D tampaknya memiliki overhead waktu kompilasi yang sedikit lebih sedikit, dan biasanya Anda tidak menghasilkan lebih banyak kode dengan template daripada yang biasanya Anda lakukan sebaliknya (Anda dapat, pada kenyataannya, berakhir dengan kode yang lebih sedikit tergantung pada keadaan).
Kubik
3
@ChristopherPfohl Saya pikir hanya Java generik yang memiliki masalah tinju / unboxing untuk tipe primitif? Reified generic C # tidak memiliki masalah.
ca9163d9
32

Lakukan 2

Ada rancangan rancangan untuk obat generik di https://blog.golang.org/go2draft .

Pergi 1

Russ Cox, salah satu veteran Go menulis posting blog berjudul The Generic Dilemma , di mana dia bertanya

… Apakah Anda ingin programmer lambat, kompiler lambat dan binari membengkak, atau waktu eksekusi lambat?

Pemrogram yang lambat bukan hasil dari generik, kompiler lambat disebabkan oleh C ++ seperti generik dan waktu eksekusi yang lambat berasal dari pendekatan tinju-unboxing yang digunakan Java.

Kemungkinan keempat yang tidak disebutkan di blog adalah menempuh rute C #. Menghasilkan kode khusus seperti di C ++, tetapi pada waktu proses saat diperlukan. Saya sangat menyukainya, tapi Go sangat berbeda dengan C # jadi ini mungkin tidak berlaku sama sekali…

Saya harus menyebutkan bahwa menggunakan Java 1.4 yang populer seperti teknik pemrograman generik dalam perjalanan yang menyebabkan interface{}menderita masalah yang persis sama dengan tinju-unboxing (karena itulah yang kami lakukan), selain hilangnya keamanan jenis waktu kompilasi. Untuk tipe kecil (seperti int) Go mengoptimalkan interface{}tipe sehingga daftar int yang ditransmisikan ke antarmuka {} menempati area memori yang berdekatan dan hanya membutuhkan ruang dua kali lebih banyak dari int normal. Namun, masih ada overhead pemeriksaan runtime saat melakukan transmisi interface{}. Referensi .

Semua project yang menambahkan dukungan generik (ada beberapa di antaranya dan semuanya menarik) secara seragam mengikuti rute C ++ untuk pembuatan kode waktu kompilasi.

pengguna7610
sumber
Solusi saya dari dilema ini adalah untuk Pergi ke default ke "waktu eksekusi lambat" dengan opsi untuk membuat profil program dan mengkompilasi ulang bagian yang paling sensitif kinerja dalam mode "kompiler lambat dan binari kembung". Sayang sekali orang yang benar-benar menerapkan hal-hal seperti itu cenderung mengambil rute C ++.
pengguna7610
1
Disebutkan bahwa tipe kecil (yaitu int) yang disimpan []interface{}menggunakan 2x RAM sebagai []int. Meskipun benar, jenis yang lebih kecil (yaitu byte) menggunakan hingga 16x RAM sebagai []byte.
BMiner
Sebenarnya tidak ada dilema dengan pendekatan C ++. Jika seorang programmer memilih untuk menulis kode template, keuntungan dari melakukannya harus membebani biaya kompilasi yang lambat. Jika tidak, dia bisa melakukannya dengan cara lama.
John Z. Li
Dilemanya adalah tentang pendekatan mana yang harus dipilih. Jika Anda menyelesaikan dilema dengan melakukan pendekatan C ++, dilema tersebut teratasi.
pengguna7610
9

Meskipun generik saat ini tidak ada di dalamnya, ada beberapa implementasi eksternal generik untuk go, yang menggunakan komentar dalam kombinasi dengan utilitas kecil yang menghasilkan kode.

Berikut adalah salah satu penerapannya: http://clipperhouse.github.io/gen/

Alexander
sumber
1

Sebenarnya menurut postingan ini :

Banyak orang telah menyimpulkan (secara keliru) bahwa posisi tim Go adalah "Go tidak akan pernah memiliki obat generik". Sebaliknya, kami memahami potensi yang dimiliki obat generik, baik untuk membuat Go jauh lebih fleksibel dan bertenaga dan membuat Go jauh lebih rumit. Jika kita ingin menambahkan obat generik, kita ingin melakukannya dengan cara yang mendapatkan fleksibilitas dan kekuatan sebanyak mungkin dengan kerumitan tambahan sesedikit mungkin.

Ayush Gupta
sumber
-1

Polimorfisme parametrik (generik) adalah dipertimbangkan untuk Go 2 .

Pendekatan ini akan memperkenalkan konsep kontrak , yang dapat digunakan untuk mengekspresikan batasan pada parameter tipe:

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

Kontrak semacam itu kemudian dapat digunakan sebagai berikut:

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

Ini adalah proposal pada tahap ini.


filter(predicate, list)Fungsi Anda dapat diimplementasikan dengan parameter tipe seperti ini:

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
}

Dalam hal ini, tidak perlu ada batasan T.

ᆼ ᆺ ᆼ
sumber
1
Jika Anda membaca jawaban ini hari ini, ketahuilah bahwa kontrak telah dibatalkan dari draf proposal: go.googlesource.com/proposal/+/refs/heads/master/design/…
jub0bs