İyi bir genel tip sistem


29

Java jeneriklerinin bazı önemli yollarla başarısız olduğu yaygın olarak kabul edilmektedir. Joker karakterlerin ve sınırların birleşimi bazı ciddi olarak okunamayan kodlara yol açtı.

Ancak, diğer dillere baktığımda, programcıların mutlu olduğu genel bir tip sistem bulamıyorum.

Aşağıdakileri böyle bir sistemin tasarım hedefleri olarak kabul edersek:

  • Her zaman kolay okunur tip bildirimleri üretir
  • Öğrenmesi kolay (kovaryans, kontravaryans vb.
  • derleme zamanı hatalarının sayısını maksimuma çıkarır

Doğru olan bir dil var mı? Google’a gidersem, gördüğüm tek şey, tür sisteminin X dilinde nasıl berbat olduğuyla ilgili şikayetlerdir. Derleme zamanında% 100 tip güvenliği doğrulamaya çalışmaktan vazgeçmeli miyiz?

Asıl sorum, bu üç amaç için en iyi "doğru olanı" yapan dildir. Bunun öznel olduğunu fark ettim, ancak şu ana kadar programcıların genel tip sisteminin bir karışıklık olduğu konusunda hemfikir olmadığı bir dili bile bulamıyorum.

Zeyilname: belirtildiği gibi, alt türleme / kalıtım ve jeneriklerin birleşimi karmaşıklığı yaratan şeydir, bu yüzden gerçekten her ikisini birleştiren ve karmaşıklığın patlamasını önleyen bir dil arıyorum.


2
Ne demek istiyorsun easy-to-read type declarations? Üçüncü kriterler de belirsizdir: örneğin, dizini endeksini sınır istisnalarının dışında derleme zamanı hatalarına dönüştürebilirim, dizini derleme zamanında hesaplayamadığım sürece dizileri indekslemenize izin vermeyebilirim. Ayrıca, ikinci ölçüt alt yazmayı dışlar. Bu mutlaka kötü bir şey değil ama ne sorduğunun farkında olmalısın.
Doval


9
@gnat, bu kesinlikle Java'ya karşı bir rant değil. Neredeyse sadece Java ile programlıyorum. Demek istediğim, genellikle Java topluluğu içinde Jeneriklerin kusurlu olduğu (tamamen başarısızlık değil, muhtemelen kısmi bir oyundur) olduğu için nasıl uygulanmaları gerektiğini sormak mantıklı bir soru. Neden yanılıyorlar ve başkaları onları doğru anladı mı? Yoksa jenerik ilaçları kesinlikle doğru olarak almak imkansız mıdır?
Peter

1
Herkes sadece C # dan çalındıysa daha az şikayet olur. Özellikle Java, kopyalayarak yetişebilecek bir konumdadır. Bunun yerine daha düşük çözümlere karar verirler. Java tasarım komitelerinin hala tartışmakta olduğu soruların çoğu C # 'da kararlaştırılmış ve uygulanmıştır. Bakmadılar bile.
usr

2
@emodendroket: C # generics hakkındaki en büyük iki şikayetimin, "süper tip" bir kısıtlama (örneğin Foo<T> where SiameseCat:T) uygulamanın bir yolu olmadığı ve dönüştürülemeyen bir jenerik tipe sahip olma ihtimalinin olmadığıObject . IMHO, .NET, yapılara benzeyen, ancak daha fazla çıplak kemikli agrega türlerinden faydalanabilir. Eğer KeyValuePair<TKey,TValue>böyle bir tip olsaydı , o zaman bir karakter IEnumerable<KeyValuePair<SiameseCat,FordFocus>>seçilebildi IEnumerable<KeyValuePair<Animal,Vehicle>>, ancak yalnızca tip kutulanamadıysa.
supercat

Yanıtlar:


24

Jenerikler, fonksiyonel programlama topluluğunda onlarca yıldır yaygın olarak bulunurken, nesne yönelimli programlama dillerine jenerik madde eklemek, özellikle alt tipleme ve jenerikliğin etkileşimi gibi bazı benzersiz zorluklar sunmaktadır.

Bununla birlikte, nesne yönelimli programlama dillerine ve özellikle de Java'ya odaklansak bile, çok daha iyi bir jenerik sistem tasarlanabilirdi:

  1. Diğer tipler nerede olursa olsun, genel türler kabul edilebilir olmalıdır. Özellikle, Tbir tür parametresiyse, aşağıdaki ifadeler uyarı vermeden derlenmelidir:

    object instanceof T; 
    T t = (T) object;
    T[] array = new T[1];
    

    Evet, bu, jeneriklerin tıpkı dildeki diğer tüm türler gibi yeniden birleştirilmesini gerektirir.

  2. Genel türün kovaryansı ve karşıtlığı, genel türün her kullanımından ziyade bildiriminde belirtilmeli (veya çıktısından) belirtilmelidir,

    Future<Provider<Integer>> s;
    Future<Provider<Number>> o = s; 
    

    ziyade

    Future<? extends Provider<Integer>> s;
    Future<? extends Provider<? extends Number>> o = s;
    
  3. Genel türler oldukça uzayabildiğinden, gereksiz olarak belirtmemize gerek kalmaz. Yani yazabilmeliyiz

    Map<String, Map<String, List<LanguageDesigner>>> map;
    for (var e : map.values()) {
        for (var list : e.values()) {
            for (var person : list) {
                greet(person);
            }
        }
    }
    

    ziyade

    Map<String, Map<String, List<LanguageDesigner>>> map;
    for (Map<String, List<LanguageDesigner>> e : map.values()) {
        for (List<LanguageDesigner> list : e.values()) {
            for (LanguageDesigner person : list) {
                greet(person);
            }
        }
    }
    
  4. Herhangi bir tip sadece referans tiplerine değil, tip parametresi olarak kabul edilebilir olmalıdır. (Eğer sahip olabilirsek int[], neden sahip olamıyoruz List<int>?)?

Bunların hepsi C # ile mümkündür.


1
Bu aynı zamanda kendinden referanslı jeneriklerden de kurtulabilir mi? Karşılaştırılabilir bir nesnenin kendisini aynı türde veya bir alt sınıftaki herhangi bir şeyle karşılaştırabileceğini söylemek istersem ne olur? Bu yapılabilir mi? Veya karşılaştırılabilir nesnelere sahip listeleri kabul eden bir sıralama yöntemi yazarsam, hepsinin birbiriyle karşılaştırılması gerekir. Enum başka iyi bir örnektir: Enum <E, Enum <E>> 'yi uzatır. Bir tip sistemin bunları yapabilmesi gerektiğini söylemiyorum, sadece C # 'un bu durumları nasıl ele aldığını merak ediyorum.
Peter

1
Java 7'nin jenerik tip çıkarımı ve C ++ 'ın bu endişelerden bazılarına otomatik olarak yardım etmesi, ancak sözdizimsel bir şeker olması ve altta yatan mekanizmaları değiştirmemesi.

@ Java Java'nın türdeki çıkarımı, gerçekten de iğrenç olmayan bazı köşe durumlarına sahiptir; ancak, genel bir yöntemi başka bir genel yönteme yönelik bir argüman olarak değerlendirirken genel olarak adsız sınıflarla çalışmamak ve joker karakterler için doğru sınırları bulamamak gibi.
Doval

@Doval bu yüzden bazı endişelere yardımcı olacağını söyledim: hiçbir şeyi düzeltmiyor ve her şeyi ele almıyor. Java jeneriklerinin birçok sorunu var: Ham türlerden daha iyi olsa da, kesinlikle birçok baş ağrısına neden oluyorlar.

34

Alt programlama, genel programlama yaparken çok fazla komplikasyon yaratır. Alt tipleri olan bir dili kullanmakta ısrar ediyorsanız, jenerik programlamanın kendisiyle birlikte gelen kesin bir karmaşıklık olduğunu kabul etmek zorundasınız. Bazı diller diğerlerinden daha iyi yapar, ancak bunu yalnızca o kadar uzağa götürebilirsiniz.

Mesela Hasker'in jenerikleriyle karşılaştır. Tip çıkarımı kullanırsanız, kazayla doğru bir jenerik fonksiyon yazabilmeniz için yeterince basittir . Tek bir türünü belirtirseniz Aslında, derleyici genellikle kendisine diyor, "Eh, oldu bu yüzden ne olursa olsun, bu jenerik yapacak, ama sadece ints bunu yapmak istedi."

Kuşkusuz, insanlar kullanmak , bazı şaşırtıcı karmaşık şekillerde Haskell'ın tip sistemini bunu her acemi yıkım yapım ancak altta yatan tür sistemi kendisi zarif ve çok takdir olduğunu.


1
Bu cevap için teşekkürler. Bu makale, Joshua Bloch'un jenerik ilaçların nerede karmaşıklaştığı ile ilgili bazı örnekleriyle başlar: artima.com/weblogs/viewpost.jsp?thread=222021 . Bu, Haskell'de bu tür yapıların iyi sayılacağı Java ve Haskell arasındaki kültürde bir fark mıdır, yoksa Haskell'in bu tür durumlardan kaçınan tip sistemi ile gerçek bir fark var mı?
Peter

10
@Peter Haskell'in alt türü yoktur ve Karl'ın dediği gibi derleyicinin "tür abir tür tamsayı olmalı " gibi kısıtlamaları içeren türleri otomatik olarak çıkarabilir .
Doval

Başka bir deyişle kovaryans , Scala gibi dillerde.
Paul Draper,

14

Generikleri yaklaşık 20 yıl önce devam eden alt tipleme ile birleştirmeye yönelik çok fazla araştırma yapıldı. Barbara Liskov'un MIT'deki araştırma grubu tarafından geliştirilen Thor programlama dili, üzerinde parametreleştirdiğiniz türün gereksinimlerini belirlemenize izin veren "where" cümleleri kavramına sahipti. (Bu, C ++ 'ın Concepts ile yapmaya çalıştığına benzer .)

Thor'un jenerikliğini ve Thor'un alt türleriyle nasıl etkileşimde bulunduklarını anlatan makale: Day, M; Gruber, R; Liskov, B; Myers, AC: Subtipler vs yan tümceler: parametrik polimorfizm kısıtlayıcı , Obj Odaklı Prog, Sys, Lang ve Apps ACM Konf , (OOPSLA-10): 156-158, 1995.

1980’lerin sonlarında Zümrüt’te yapılan çalışmaların inşa edildiğine inanıyorum. (Bu çalışmayı okumamıştım, ancak referans: Siyah, A; Hutchinson, N; Jul, E; Levy, H; Carter, L: Zümrüt Dağılım ve Soyut Tipleri , _IEEE T. Yazılım Müh., 13 ( 1): 65-76, 1987.

Hem Thor hem de Emerald “akademik diller” idi, bu yüzden insanların cümlelerin (kavramların) herhangi bir gerçek sorunu çözüp çözmediğini gerçekten anlamaları için muhtemelen yeterli bir kullanım elde edemediler. Bjarne Stroustrup'un C ++ 'da ilk Kavramları denemesinin neden başarısız olduğuna dair makalesini okumak ilginç: Stroustrup, B: C ++ 0x "Kavramları Kaldır" Kararı , Dr Dobbs , 22 Temmuz 2009. ( Stroustrup ana sayfasında daha fazla bilgi . )

İnsanların deniyor gibi göründüğü bir başka yön, özellik denilen bir şeydir . Örneğin Mozilla'nın Rust programlama dili özellikleri kullanır. Anladığım kadarıyla (tamamen yanlış olabilir), bir sınıfın bir özelliği sağladığını ilan etmek, bir sınıfın bir arayüz oluşturduğunu söylemek gibi bir şeydir, ancak siz "bir" yerine "gibi" davrandığını söylüyorsunuz. Apple'ın yeni Swift programlama dilleri benzer bir kavram kullanıyor görünüyor protokolleri için jenerik parametrelere kısıtlamaları belirleyen .

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.