Go'da neden jenerik ilaç yok?


126

Sorumluluk reddi: Go ile yalnızca bir gündür oynadım, bu yüzden çok şey kaçırmış olma ihtimalim çok yüksek.

Go'da jenerikler / şablonlar / whatsInAName için neden gerçek bir destek olmadığını bilen var mı? Yani bir jenerik var map, ancak bu derleyici tarafından sağlanırken bir Go programcısı kendi uygulamasını yazamaz. Go'yu olabildiğince ortogonal yapmakla ilgili tüm konuşmalarla birlikte, neden genel bir tür kullanıp yeni bir tane OLUŞTURMAYIYORUM?

Özellikle işlevsel programlama söz konusu olduğunda, lambdalar, hatta kapanışlar vardır, ancak jenerikten yoksun statik tip bir sistemle, nasıl, iyi, genel yüksek dereceli fonksiyonları gibi yazabilirim filter(predicate, list)? Tamam, Bağlantılı listeler ve benzerleri interface{}tür güvenliğinden ödün vererek yapılabilir .

SO / Google üzerinde hızlı bir arama herhangi bir içgörü ortaya koymadığından, jenerikler Go'ya sonradan eklenecek gibi görünüyor. Java çalışanlarından çok daha iyisini yapacağına dair Thompson'a güveniyorum, ama neden jenerik ilaçları dışarıda tutalım? Yoksa planlandılar ve henüz uygulanmadı mı?


Bence şunu belirtmeye değer: {} arabirimini kullanmak tür güvenliğinden ödün vermez. Bu bir türdür ve diğer türlere ileri sürülebilir (dönüştürülmez), ancak bu iddialar yine de tür güvenliğini korumak için çalışma zamanı denetimlerini çağırır.
cthom06

12
interface{}statik tip güvenliği feda eder . Bununla birlikte, Scheme'den bahsederken bir sonraki paragraftan bahsederken yapılması biraz garip bir şikayettir, çünkü Scheme normalde statik tip kontrolüne sahip değildir.
poolie

@poolie: Benim ilgilendiğim şey, bir dil içindeki TEK paradigmaya bağlı kalmak. Ya ben statik tip güvenlik XOR kullanmıyorum.

2
btw, golang.org'da görebileceğiniz gibi 'GO' değil, 'Go' olarak yazılır. Ve büyük / küçük harfe duyarlıdır. :-)
poolie

Yanıtlar:


78

bu cevabı burada bulacaksınız: http://golang.org/doc/faq#generics

Go'nun neden genel türleri yok?

Jenerikler bir noktada eklenebilir. Bazı programcıların bunu yaptığını bilsek de, onlar için bir aciliyet hissetmiyoruz.

Jenerikler kullanışlıdır, ancak tip sistem ve çalışma süresinde karmaşıklık açısından bir maliyete sahiptirler. Karmaşıklığa orantılı değer veren bir tasarım henüz bulamadık, ancak üzerinde düşünmeye devam ediyoruz. Bu arada, Go'nun yerleşik haritaları ve dilimlerinin yanı sıra, kaplar oluşturmak için boş arabirimi kullanma yeteneği (açık kutudan çıkarma ile), çoğu durumda jeneriklerin daha az sorunsuz olsa da etkinleştireceği şeyi yapan kod yazmanın mümkün olduğu anlamına gelir.

Bu açık bir sorun olmaya devam ediyor.


14
@amoebe, "boş arabirim", interface{}en temel arabirim türüdür ve her nesne bunu sağlar. Onları tutan bir kap yaparsanız, herhangi bir (ilkel olmayan) nesneyi kabul edebilir. Yani ObjectsJava'da tutulan bir konteynere çok benziyor .
poolie

4
@YinWang Generics, türden çıkarılan bir ortamda o kadar basit değildir. Daha önemlisi; arabirim {}, C'deki void * işaretçilerine eşdeğer değildir. Daha iyi benzetmeler, C # 'ın System.Object veya Objective-C'nin kimlik türleri olacaktır. Tür bilgisi korunur ve somut türüne "döndürülebilir" (aslında iddia edilebilir). Burada cesur bilgilerini alın: golang.org/ref/spec#Type_assertions
tbone

2
@tbone C # 'ın System.Object (veya Java's Object per se) aslında "C'nin boşluk işaretçileri" ile kastettiğim şeydir (bu dillerde işaretçi aritmetiğini yapamayacağınız kısmı göz ardı ederek). Statik tip bilginin kaybolduğu yerlerdir. Bir cast, bir çalışma zamanı hatası alacağınız için pek yardımcı olmaz.
Ian

1
@ChristopherPfohl D'nin şablonları biraz daha az derleme süresi ek yüküne sahip gibi görünüyor ve normalde şablonlarla normalde yapacağınızdan daha fazla kod üretmiyorsunuz (aslında koşullara bağlı olarak daha az kod elde edebilirsiniz).
Kübik

3
@ChristopherPfohl Sadece Java jeneriklerinin ilkel türler için kutulama / kutudan çıkarma sorunu olduğunu düşünüyorum. C # 'nin reified generic sorunu yok.
ca9163d9

32

2 gidin

Https://blog.golang.org/go2draft adresinde jenerikler için bir taslak tasarım bulunmaktadır .

1 gidin

Go gazilerinden biri olan Russ Cox, The Generic Dilemma adlı bir blog yazısı yazdı ve burada

… Yavaş programcılar, yavaş derleyiciler ve şişirilmiş ikili dosyalar mı yoksa yavaş yürütme süreleri mi istiyorsunuz?

Yavaş programcılar jenerik olmamasının sonucu değildir, yavaş derleyiciler C ++ benzeri jeneriklerden kaynaklanır ve yavaş yürütme süreleri Java'nın kullandığı kutudan çıkarma yaklaşımından kaynaklanır.

Blogda belirtilmeyen dördüncü olasılık C # yoluna gitmektir. Özel kodu C ++ 'da olduğu gibi, ancak gerektiğinde çalışma zamanında üretmek. Gerçekten beğendim, ancak Go, C # 'den çok farklıdır, bu yüzden bu muhtemelen hiç uygulanamaz ...

Ben tekniği gibi popüler Java 1.4 kullanarak bahsetmeliyiz seferde jenerik programlama buna atmalarını interface{}tamamen aynı sorunlardan muzdarip boks-kutudan çıkarma derleme zamanı tür güvenliği kaybının yanı sıra, (yani ne yapıyoruz çünkü). Küçük türler için (ints gibi) Go, interface{}türü, arabirime dönüştürülen girişlerin listesi {} bitişik bir bellek alanını kaplayacak ve normal inçlere göre yalnızca iki kat daha fazla yer kaplayacak şekilde optimize eder . Yine de, içinden yayın yaparken çalışma zamanı kontrollerinin ek yükü var interface{}. Referans .

Gitmek için genel destek ekleyen tüm projeler (bunlardan birkaçı var ve hepsi ilginç) tek tip olarak derleme zamanı kodu oluşturmanın C ++ yoluna gider.


Bu ikilem için benim çözümüm, "Yavaş derleyiciler ve şişirilmiş ikili dosyalar" modunda programı profilleme ve performansa en duyarlı parçaları yeniden derleme seçeneği ile varsayılana git "yavaş yürütme sürelerine" gitmektir. Gerçekten böyle şeyler uygulayan insanların C ++ yolunu izlemeleri çok kötü.
user7610

1
Depolanan küçük türlerin (yani int) []interface{}RAM'in 2 katı olarak kullanıldığından bahsedildi []int. Doğru olsa da, daha küçük türler (yani bayt) bile 16x'e kadar RAM kullanır []byte.
BMiner

Aslında C ++ yaklaşımıyla ilgili bir ikilem yoktur. Bir programcı şablon kodu yazmayı seçerse, bunu yapmanın yararı, yavaş derlemenin maliyetini aşmalıdır. Aksi takdirde, bunu eski yöntemle yapabilirdi.
John Z. Li

İkilem, hangi yaklaşımı seçeceğinizle ilgilidir. C ++ yaklaşımını kullanarak ikilemi çözerseniz, ikilem çözülür.
user7610

9

Jenerikler halihazırda yerleşik olmasa da, kod üreten küçük yardımcı programlarla kombinasyonlar halinde yorum kullanan go için birkaç harici jenerik uygulaması vardır.

İşte böyle bir uygulama: http://clipperhouse.github.io/gen/


1

Aslında bu gönderiye göre :

Pek çok kişi (yanlış bir şekilde) Go ekibinin pozisyonunun "Git hiçbir zaman jeneriklere sahip olmayacağı" sonucuna varmıştır. Aksine, hem Go'yu çok daha esnek ve güçlü kılmak hem de Go'yu çok daha karmaşık hale getirmek için potansiyel jeneriklerin sahip olduğunu anlıyoruz. Jenerik ekleyeceksek, bunu olabildiğince az karmaşıklıkla daha fazla esneklik ve güç elde edecek şekilde yapmak istiyoruz.


-1

Parametrik polimorfizmi (jenerikler) olduğu için göz altında Go 2 .

Bu yaklaşım , tür parametreleri üzerindeki kısıtlamaları ifade etmek için kullanılabilecek bir sözleşme kavramını ortaya koyacaktır:

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

Böyle bir sözleşme daha sonra şu şekilde kullanılabilir:

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

Bu, bu aşamada bir tekliftir .


Sizin filter(predicate, list)fonksiyonu böyle bir tip parametresi ile uygulanabilir:

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
}

Bu durumda sınırlamaya gerek yoktur T.


1
Bugün bu yanıtı okuyorsanız, sözleşmelerin taslak tekliften çıkarıldığını
unutmayın
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.