Perché l'overloading non è consentito con i tipi di ritorno? (almeno nelle lingue di solito usate)

6

Non conosco tutti i linguaggi di programmazione, ma è chiaro che solitamente la possibilità di sovraccaricare un metodo prendendo in considerazione il suo tipo di ritorno (assumendo che gli argomenti siano lo stesso numero e tipo) non è supportata.

Intendo qualcosa del genere:

 int method1 (int num)
 {

 }
 long method1 (int num)
 {

 }

Non è che sia un grosso problema per la programmazione, ma in alcune occasioni l'avrei gradito.

Ovviamente non ci sarebbe alcun modo per quei linguaggi di supportarlo senza un modo per differenziare quale metodo viene chiamato, ma la sintassi per quella può essere semplice come qualcosa come [int] metodo1 (num) o [lungo] metodo1 (Num) in questo modo il compilatore saprebbe quale sarebbe quello che verrebbe chiamato.

Non so come funzionano i compilatori ma non sembra così difficile da fare, quindi mi chiedo perché una cosa del genere non venga solitamente implementata.

Quali sono i motivi per cui qualcosa del genere non è supportato?

    
posta user2638180 28.04.2016 - 22:55
fonte

4 risposte

12

Complifica il controllo dei tipi.

Quando autorizzi il sovraccarico solo in base ai tipi di argomento e permetti solo di dedurre i tipi di variabili dai loro inizializzatori, tutte le informazioni del tipo fluiscono in un'unica direzione: la struttura della sintassi.

var x = f();

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

Quando si consente alle informazioni sul tipo di viaggiare in entrambe le direzioni , come la deduzione del tipo di una variabile dal suo utilizzo, è necessario un risolutore di vincoli (come Algorithm W, per i sistemi di tipo Hindley-Milner) in per determinare il tipo.

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]

Qui, dovevamo lasciare il tipo di x come una variabile di tipo non risolta ∃T , dove tutto ciò che sappiamo è che è parsabile. Solo più tardi, quando x viene utilizzato in un tipo concreto, disponiamo di informazioni sufficienti per risolvere il vincolo e determinare che ∃T = int , che propaga le informazioni sul tipo in basso dell'albero di sintassi dall'espressione di chiamata in x .

Se non riusciamo a determinare il tipo di x , anche questo codice verrebbe sovraccaricato (quindi il chiamante determinerebbe il tipo) o dovremmo segnalare un errore sull'ambiguità.

Da questo, un designer di linguaggio può concludere:

  • Aggiunge complessità all'implementazione.

  • Rende tipologicamente i casi patologici più lenti, in modo esponenziale.

  • È più difficile produrre buoni messaggi di errore.

  • È troppo diverso dallo status quo.

  • Non ho voglia di implementarlo.

risposta data 29.04.2016 - 01:12
fonte
2

Perché è ambiguo. Usando C # come esempio:

var foo = method(42);

Quale sovraccarico dovremmo usare?

Ok, forse è stato un po 'ingiusto. Il compilatore non può capire quale tipo usare se non lo diciamo nel tuo linguaggio ipotetico. Quindi, la digitazione implicita è impossibile nella tua lingua e ci sono metodi anonimi e Linq insieme ...

Che ne dici di questo? (Firme leggermente ridefinite per illustrare il punto.)

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

....

long foo = method(42);

Dovremmo usare il sovraccarico int o il sovraccarico short ? Semplicemente non lo sappiamo, dovremo specificarlo con la sintassi [int] method1(num) . Il che è un po 'difficile da analizzare e scrivere per essere onesti.

long foo = [int] method(42);

Il fatto è che la sintassi sorprendentemente simile a un metodo generico in C #.

long foo = method<int>(42);

(C ++ e Java hanno caratteristiche simili.)

In breve, i progettisti di linguaggi hanno scelto di risolvere il problema in un modo diverso per semplificare l'analisi e attivare funzionalità linguistiche molto più potenti.

Dici di non sapere molto sui compilatori. Consiglio vivamente di imparare su grammatiche e parser. Una volta capito che cos'è una grammatica senza contesto, avrai un'idea molto più chiara del perché l'ambiguità è una cosa negativa.

    
risposta data 29.04.2016 - 01:32
fonte
0

Tutte le funzionalità linguistiche aggiungono complessità, quindi devono fornire un vantaggio sufficiente a giustificare l'inevitabile trucchetto, casi angusti e confusione dell'utente che ogni funzione crea. Per la maggior parte delle lingue, questo semplicemente non fornisce un vantaggio sufficiente per giustificarlo.

Nella maggior parte delle lingue, ci si aspetterebbe che l'espressione method1(2) abbia un tipo definito e un valore di ritorno più o meno prevedibile. Ma se permetti il sovraccarico sui valori di ritorno, significa che è impossibile dire cosa significhi quell'espressione in generale senza considerare il contesto circostante. Considera cosa succede quando hai un metodo unsigned long long foo() la cui implementazione termina con return method1(2) ? Dovrebbe questo chiamare il sovraccarico di long -returning o il sovraccarico di int -returning o semplicemente dare un errore del compilatore?

Inoltre, se devi aiutare il compilatore annotando il tipo restituito, non solo stai inventando più sintassi (il che solleva tutti i summenzionati costi di consentire la funzionalità a tutti), ma stai effettivamente facendo lo stesso cosa come la creazione di due metodi con nomi diversi in un linguaggio "normale". [long] method1(2) più intuitivo di long_method1(2) ?

D'altra parte, alcuni linguaggi funzionali come Haskell con sistemi di tipi statici molto forti consentono questo tipo di comportamento, perché la loro inferenza di tipo è abbastanza potente da rendere raramente necessario annotare il tipo di ritorno in quelle lingue. Ma ciò è possibile solo perché tali linguaggi applicano realmente la sicurezza del tipo, più di qualsiasi altro linguaggio tradizionale, oltre a richiedere che tutte le funzioni siano pure e referenzialmente trasparenti. Non è qualcosa che sarebbe mai fattibile nella maggior parte dei linguaggi OOP.

    
risposta data 28.04.2016 - 23:18
fonte
-4
int main() {
    auto var1 = method1(1);
}
    
risposta data 29.04.2016 - 00:01
fonte

Leggi altre domande sui tag