Geri dönüş tipleriyle aşırı yüklemeye neden izin verilmiyor? (en azından genellikle kullanılan dillerde)


9

Tüm programlama dillerini bilmiyorum, ancak genellikle dönüş yöntemini (argümanlarının aynı sayı ve tür olduğu varsayılarak) dikkate alarak bir yöntemi aşırı yükleme olasılığının desteklenmediği açıktır.

Böyle bir şey demek istiyorum:

 int method1 (int num)
 {

 }
 long method1 (int num)
 {

 }

Programlama için büyük bir sorun değil, ancak bazı durumlarda bunu memnuniyetle karşılarım.

Açıkçası, bu dillerin hangi yöntemin çağrıldığını ayırt etmenin bir yolu olmadan bunu desteklemesinin bir yolu olmayacaktır, ancak bunun sözdizimi [int] yöntem1 (num) veya [uzun] yöntem1 (num) gibi basit olabilir. bu şekilde derleyici hangisinin çağrılacağını bilirdi.

Derleyicilerin nasıl çalıştığını bilmiyorum ama bu o kadar zor görünmüyor, bu yüzden neden böyle bir şeyin genellikle uygulanmadığını merak ediyorum.

Böyle bir şeyin desteklenmemesinin nedenleri nelerdir?


Belki de iki geri dönüş türü arasında örtük dönüşümlerin bulunmadığı bir örnekle sorunuz daha iyi olurdu - örneğin, sınıflar Foove Bar.

Peki, böyle bir özellik neden yararlı olabilir?
James Youngman

3
@JamesYoungman: Örneğin, dizeleri farklı türlere ayrıştırmak için, int read (String s), float read (String s) vb. Bir yönteminiz olabilir. Yöntemin aşırı yüklenmiş her varyasyonu, uygun tip için bir ayrıştırma gerçekleştirir.
Giorgio

Bu sadece statik olarak yazılmış dillerle ilgili bir sorundur. Birden fazla dönüş türüne sahip olmak, Javascript veya Python gibi dinamik olarak yazılan dillerde oldukça rutindir.
Robot Gort

1
@StevenBurnap, um, hayır. Örneğin JavaScript ile, işlev aşırı yüklemesi yapamazsınız. Bu aslında işlev adının aşırı yüklenmesini destekleyen dillerle ilgili bir sorundur.
David Arno

Yanıtlar:


19

Tip kontrolünü zorlaştırır.

Yalnızca bağımsız değişken türlerine dayalı olarak aşırı yüklemeye izin verirken ve yalnızca değişken türlerini başlatıcılarından çıkarmaya izin verirseniz, tüm tür bilgileriniz tek bir yönde akar: sözdizimi ağacına kadar.

var x = f();

given      f   : () -> int  [upward]
given      ()  : ()         [upward]
therefore  f() : int        [upward]
therefore  x   : int        [upward]

Bir değişkenin türünün kullanımından çıkarılması gibi tür bilgilerinin her iki yönde de seyahat etmesine izin verdiğinizde , türü belirlemek için bir kısıtlama çözücüsüne (örneğin, Hindley-Milner türü sistemler için Algoritma W) ihtiyacınız vardır.

var x = parse("123");
print_int(x);

given      parse        : string -> T  [upward]
given      "123"        : string       [upward]
therefore  parse("123") : ∃T           [upward]
therefore  x            : ∃T           [upward]
given      print_int    : int -> ()    [upward]
therefore  print_int(x) : ()           [upward]
therefore  int -> ()    = ∃T -> ()     [downward]
therefore  ∃T           = int          [downward]
therefore  x            : int          [downward]

Burada, xçözülmemiş bir tür değişkeni türünü terk etmemiz gerekiyordu ∃T, burada bildiğimiz tek şey bunun ayrıştırılabilir olmasıdır. Ancak daha sonra, xsomut bir tipte kullanıldığında, kısıtlamayı çözmek ve ∃T = inttip bilgisini sözdizim ağacından aşağıya çağrı ifadesinden yaymak için yeterli bilgiye sahibiz x.

Türünü belirleyemezsek, xbu kod da aşırı yüklenir (böylece arayan türü belirleyecektir) veya belirsizlik hakkında bir hata bildirmek zorunda kalacağız.

Bundan bir dil tasarımcısı şu sonuca varabilir:

  • Uygulamaya karmaşıklık katar.

  • Daktilo kontrolünü yavaşlatır - patolojik durumlarda üstel olarak.

  • İyi hata mesajları üretmek daha zordur.

  • Statükodan çok farklı.

  • Uygulamak istemiyorum.


1
Ayrıca: Bazı durumlarda derleyicinizin programcının kesinlikle beklemediği seçimler yapacağını anlamak zor olacaktır, bu da hataların bulunmasına yol açmaktadır.
gnasher729

1
@ gnasher729: Kabul etmiyorum. Bu özelliğe sahip Haskell'i düzenli olarak kullanıyorum ve aşırı yükleme (viz. Typeclass örneği) seçiminden asla ısırılmadım. Bir şey belirsizse, sadece bir tür ek açıklama eklemeye zorlar. Ve hala benim dilime tam tip çıkarım yapmayı seçtim çünkü inanılmaz derecede faydalı. Bu cevap bana şeytanın avukatıydı.
Jon Purdy

4

Çünkü belirsiz. Örnek olarak C # kullanma:

var foo = method(42);

Hangi aşırı yükü kullanmalıyız?

Tamam, belki de bu biraz haksızlıktı. Derleyici, varsayımsal dilinizde söylemezsek ne tür kullanacağını bulamaz. Yani, örtük yazma sizin dilinizde imkansızdır ve anonim yöntemler ve Linq ile birlikte gider ...

Buna ne dersin? (Noktayı göstermek için imzaları hafifçe yeniden tanımladı.)

short method(int num) { ... }
int method(int num) { ... }

....

long foo = method(42);

intAşırı shortyüklenmeyi mi yoksa aşırı yüklenmeyi mi kullanmalıyız ? Sadece bilmiyoruz, [int] method1(num)sözdiziminizle belirtmemiz gerekecek . Dürüst olmak gerekirse ayrıştırmak ve yazmak biraz acıdır .

long foo = [int] method(42);

Mesele şu ki, şaşırtıcı bir şekilde C # 'daki genel bir yönteme benzer.

long foo = method<int>(42);

(C ++ ve Java benzer özelliklere sahiptir.)

Kısacası, dil tasarımcıları ayrışmayı basitleştirmek ve çok daha güçlü dil özelliklerini etkinleştirmek için sorunu farklı bir şekilde çözmeyi seçti.

Derleyiciler hakkında fazla bir şey bilmediğinizi söylüyorsunuz. Dilbilgileri ve ayrıştırıcılar hakkında bilgi almanızı şiddetle tavsiye ederim. Bağlamdan bağımsız bir dilbilgisinin ne olduğunu anladıktan sonra, belirsizliğin neden kötü bir şey olduğu hakkında çok daha iyi bir fikriniz olacaktır.


Jenerikler hakkında iyi bir nokta, ama kafa karıştırıcı görünüyorsun shortve int.
Robert Harvey

Evet @RobertHarvey haklısın. Konuyu açıklamak için bir örnek için mücadele ediyordum. methodBir kısa veya int döndürülürse daha iyi çalışır ve tür uzun olarak tanımlanır.
RubberDuck

Biraz daha iyi görünüyor.
RubberDuck

Dönüş türü aşırı yüklemesi olan bir dilde tür çıkarımı, anonim alt yordamlar veya monad kavrayışınız olamaz argümanlarınızı almıyorum. Haskell bunu yapar ve Haskell üçüne de sahiptir. Ayrıca parametrik polimorfizm vardır. long/ int/ shortİle ilgili amacınız , alt tür ve / veya örtük dönüşümlerin karmaşıklığı hakkında, dönüş türü aşırı yüklemesinden daha fazladır. Sonuçta, sayı değişmezleri vardır onların dönüş C tipi ++, Java, C♯ ve diğerleri üzerinde aşırı ve bu bir sorun mevcut görünmüyor. Bir kural oluşturabilirsiniz: örneğin, en belirgin / genel türü seçin.
Jörg W Mittag

Demek istediğim, imkansız hale getirmek değil, işleri gereksiz yere karmaşık hale getirmek değildi.
RubberDuck

0

Tüm dil özellikleri karmaşıklık katar, bu nedenle her özelliğin oluşturduğu kaçınılmaz gotchas, köşe vakaları ve kullanıcı karışıklığını haklı çıkarmak için yeterli fayda sağlamaları gerekir. Çoğu dil için, bu sadece onu haklı çıkarmak için yeterli fayda sağlamaz.

Çoğu dilde, ifadenin method1(2)belirli bir türe ve az çok öngörülebilir bir dönüş değerine sahip olmasını beklersiniz . Ancak, dönüş değerlerinde aşırı yüklemeye izin verirseniz, bu, etrafındaki bağlamı dikkate almadan ifadenin genel olarak ne anlama geldiğini söylemek imkansızdır. unsigned long long foo()Uygulaması sona eren bir yönteminiz olduğunda ne olacağını düşünün return method1(2)? Bu, long-returning aşırı yükünü veya -returning aşırı yükünü çağırmalı mı intyoksa sadece bir derleyici hatası mı vermeli ?

Ayrıca, dönüş türüne ek açıklama ekleyerek derleyiciye yardımcı olmanız gerekiyorsa, yalnızca daha fazla sözdizimi icat etmekle kalmazsınız (bu, özelliğin var olmasına izin vermenin tüm yukarıda belirtilen maliyetlerini yükseltir), aynı zamanda oluşturma ile aynı şeyi etkili bir şekilde yaparsınız. "normal" bir dilde iki farklı adlandırılmış yöntem. Mı [long] method1(2)daha sezgisel long_method1(2)?


Öte yandan, çok güçlü statik tip sistemlere sahip Haskell gibi bazı fonksiyonel diller bu tür davranışlara izin verir, çünkü tür çıkarımları bu dillerdeki dönüş türüne nadiren açıklama eklemeniz gereken kadar güçlüdür. Ancak bu sadece mümkündür çünkü bu diller, tüm fonksiyonların saf ve referans olarak şeffaf olmasını gerektirdiği gibi, herhangi bir geleneksel dilden daha çok tip güvenliğini gerçekten zorlar. Çoğu OOP dilinde mümkün olacak bir şey değil.


2
"tüm fonksiyonların saf ve referans olarak şeffaf olmasını zorunlu kılmanın yanında": Bu, dönüş tipi aşırı yüklemeyi nasıl kolaylaştırır?
Giorgio

@Giorgio Yapmaz - Rust fonksiyon saflığını zorlamaz ve yine de aşırı tip dönüş yapabilir (Rust'daki aşırı yüklenmesi diğer dillerden çok farklı olsa da (sadece şablonları kullanarak aşırı yükleyebilirsiniz))
Idan Arye

[Long] ve [int] kısmı yöntemi açık bir şekilde çağırmanın bir yoluna sahipti, çoğu durumda nasıl çağrılması gerektiği doğrudan yöntemin yürütülmesine atanan değişkenin türünden çıkarılabilir.
user2638180

0

O ise Swift mevcuttur ve ince orada çalışıyor. Açıkçası her iki tarafta da belirsiz bir tip olamaz, bu yüzden solda bilinmelidir.

Bunu basit bir kodlama / kod çözme API'sinde kullandım .

public protocol HierDecoder {
  func dech() throws -> String
  func dech() throws -> Int
  func dech() throws -> Bool

Bu init, bir nesnenin gibi parametre türlerinin bilindiği çağrıların çok basit çalıştığı anlamına gelir :

    private static let typeCode = "ds"
    static func registerFactory() {
        HierCodableFactories.Register(key:typeCode) {
            (from) -> HierCodable in
            return try tgDrawStyle(strokeColor:from.dech(), fillColor:from.dechOpt(), lineWidth:from.dech(), glowWidth: from.dech())
        }
    }
    func typeKey() -> String { return tgDrawStyle.typeCode }
    func encode(to:HierEncoder) {
        to.ench(strokeColor)
        to.enchOpt(fillColor)
        to.ench(lineWidth)
        to.ench(glowWidth)
    }

Eğer yakından ilgileniyorsanız, dechOptyukarıdaki çağrıyı göreceksiniz . Ben farklılaştırıcı bir isteğe bağlı iade aynı işlev adını aşırı zor çağıran bağlam isteğe bağlı bir beklenti getirebilir gibi hataya çok eğilimli olduğunu öğrendim.


-5
int main() {
    auto var1 = method1(1);
}

Bu durumda derleyici, a) belirsiz olduğu için çağrıyı reddedebilir b) ilk / sonuncuyu seçer c) türünü bırakıp var1tür çıkarımı ile devam eder ve diğer bazı ifadeler var1, doğru uygulama. Sonuç olarak, tür çıkarımının önemsiz olduğu bir durumu göstermek, bu tür çıkarımdan başka bir noktanın nadiren kanıtlanmasının genellikle önemsiz olduğunu göstermektedir.
back2dos

1
Güçlü bir argüman değil. Örneğin pas, tip çıkarımını yoğun bir şekilde kullanır ve bazı durumlarda, özellikle jenerik ilaçlarla, hangi tipe ihtiyacınız olduğunu söyleyemez. Bu gibi durumlarda, tür çıkarımına güvenmek yerine, türü açıkça belirtmeniz gerekir.
8bittree

1
Uh ... bu aşırı yüklenmiş bir dönüş tipi göstermiyor. method1belirli bir türü döndürmek için bildirilmelidir.
Robot Gort
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.