Perché Scala richiede che le funzioni abbiano un tipo di ritorno esplicito?


11

Di recente ho iniziato a imparare a programmare a Scala, ed è stato divertente finora. Mi piace molto la capacità di dichiarare funzioni all'interno di un'altra funzione che sembra semplicemente cosa intuitiva da fare.

Una piscia che ho su Scala è il fatto che Scala richiede un tipo di ritorno esplicito nelle sue funzioni . E mi sento come questo ostacola l'espressività della lingua. Inoltre è difficile programmare con questo requisito. Forse è perché vengo dalla zona di comfort Javascript e Ruby. Ma per un linguaggio come Scala che avrà tonnellate di funzioni connesse in un'applicazione, non riesco a immaginare come faccio a brainstorming nella mia testa esattamente quale tipo di funzione particolare che sto scrivendo dovrebbe tornare con le ricorsioni dopo le ricorsioni.

Questo requisito di dichiarazione esplicita del tipo di ritorno sulle funzioni non mi dà fastidio per linguaggi come Java e C ++. Le ricorsioni in Java e C ++, quando avvenivano, spesso venivano gestite da 2 a 3 funzioni max. Mai più funzioni concatenate insieme come la Scala.

Quindi immagino di chiedermi se ci sia una buona ragione per cui Scala dovrebbe avere il requisito di funzioni con tipo di ritorno esplicito?


5
Non lo fa - e non riesco a capire perché sarebbe un problema se lo facesse.
Keith Thompson,

1
Ho pensato di si. Ci sono casi in Scala in cui il tipo restituito per una funzione è in realtà ambiguo?
raccolta dei rifiuti

Risposte:


15

Scala non richiede un tipo di ritorno esplicito su tutte le funzioni, solo quelle ricorsive. La ragione di ciò è che l'algoritmo di inferenza del tipo di Scala è (qualcosa di simile a) una semplice scansione dall'inizio alla fine che non è in grado di guardare.

Ciò significa che una funzione come questa:

def fortuneCookieJoke(message: String) = message + " in bed."

non ha bisogno di un tipo restituito, dal momento che il compilatore Scala può vedere chiaramente, senza usare variabili logiche o guardare qualcosa di diverso dai parametri del metodo, che deve essere il tipo restituito String.

D'altra parte, una funzione come questa:

def mapInts(f: (Int) => Int, l: List[Int]) = l match {
  case Nil => Nil
  case x :: xs => f(x) :: mapInts(f, xs)
}

causerà un errore in fase di compilazione, poiché il compilatore Scala non può vedere, senza usare né lookahead né variabili logiche, esattamente quale sia il tipo di mapInts. Il massimo che potrebbe dire, se fosse abbastanza intelligente, è che il tipo restituito è un supertipo di List[Nothing], poiché Nilè di quel tipo. Ciò non fornisce informazioni abbastanza vicine da determinare con precisione il tipo di reso di mapInts.

Si noti che questo è specifico di Scala e che esistono altri linguaggi tipizzati staticamente (la maggior parte della famiglia Miranda / Haskell / Clean, la maggior parte della famiglia ML e alcuni altri sparsi) che utilizzano algoritmi di inferenza del tipo molto più completi e capaci di Scala. Inoltre, tieni presente che questo non è interamente colpa di Scala; il sottotipo nominale e l'inferenza del tipo di intero modulo sono fondamentalmente in contrasto tra loro, e i progettisti di Scala hanno scelto di favorire il primo rispetto al secondo per motivi di compatibilità Java, mentre i linguaggi funzionali "più puri" tipicamente statici sono stati per lo più progettati con scelta opposta in mente.


4
In realtà, il problema non è tanto capire un algoritmo di inferenza di tipo più completo. Sta scoprendo un algoritmo di inferenza di tipo più completo, pur mantenendo l'alta qualità dei messaggi di errore nell'attuale compilatore Scala.
Jörg W Mittag,

1
Il ritorno corretto per case Nilnon sarebbe effettivamente un vuoto List[Int]()? Nel qual caso un compilatore sufficientemente intelligente potrebbe capirlo. Questo è tutto il ruolo di Devil's Advocate, suppongo.
KChaloux,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.