Motivi per la rimozione dei tipi di funzione in Java 8


12

Ho cercato di capire perché JDK 8 Lambda Expert Group (EG) ha deciso di non includere un nuovo tipo di funzione nel linguaggio di programmazione Java.

Scorrendo la mailing list ho trovato un thread con la discussione sulla rimozione dei tipi di funzione .

Molte delle dichiarazioni sono ambigue per me, forse a causa della mancanza di contesto e in alcuni casi a causa della mia conoscenza limitata sull'implementazione dei sistemi di tipo.

Tuttavia, ci sono un paio di domande che credo di poter formulare in modo sicuro in questo sito per aiutarmi a capire meglio cosa significassero.

So che potrei porre le domande nella mailing list, ma il thread è vecchio e tutte le decisioni sono già state prese, quindi è probabile che io sarò ignorato, soprattutto vedendo che questi ragazzi sono già in ritardo con i loro piani.

Nella sua risposta per supportare la rimozione dei tipi di funzione e approvare l'uso dei tipi SAM, Brian Goetz afferma:

Nessuna reificazione. C'è stato un lungo thread su quanto sarebbe utile reificare i tipi di funzione. Senza reificazione, i tipi di funzione sono ostacolati.

Non sono riuscito a trovare il filo che menziona. Ora, posso capire che l'introduzione di un tipo di funzione strutturale può comportare alcune complicazioni nel sistema di tipo prevalentemente nominale Java, quello che non riesco a capire è come i tipi SAM parametrizzati sono diversi in termini di reificazione.

Non sono entrambi soggetti agli stessi problemi di reificazione? Qualcuno comprende in che modo i tipi di funzione sono diversi dai tipi SAM parametrizzati in termini di reificazione?

In un altro commento Goetz dice:

Esistono due approcci di base alla digitazione: nominale e strutturale. L'identità di un nominale si basa sul suo nome; l'identità di un tipo strutturale si basa su ciò di cui è composta (come "tupla di int, int" o "funzione da int a float"). La maggior parte delle lingue sceglie principalmente nominali o prevalentemente strutturali; non ci sono molte lingue che mescolano con successo la tipizzazione nominale e strutturale tranne "attorno ai bordi". Java è quasi interamente nominale (con poche eccezioni: le matrici sono di tipo strutturale, ma in fondo esiste sempre un tipo di elemento nominale; i generici hanno anche un mix di nominale e strutturale, e questo è in realtà parte della fonte di molti delle lamentele della gente riguardo ai generici.) Innesto di un sistema di tipo strutturale (tipi di funzione) su Java " s sistema di tipo nominale significa nuova complessità e casi limite. Vale la pena beneficiare dei tipi di funzione?

Quelli di voi con esperienza nell'implementazione di sistemi di tipi. Conosci qualche esempio di queste complessità o casi limite che menziona qui?

Onestamente mi confondo con queste accuse quando ritengo che un linguaggio di programmazione come Scala, che è interamente basato sulla JVM, abbia il supporto di tipi strutturali come funzioni e tuple, anche con i problemi di reificazione della piattaforma sottostante.

Non fraintendetemi, non sto dicendo che un tipo di funzione dovrebbe essere migliore dei tipi SAM. Voglio solo capire perché hanno preso questa decisione.


4
"Innestare un sistema di tipi strutturali (tipi di funzione) sul sistema di tipi nominali di Java significa nuova complessità" - questo molto probabilmente era inteso come complessità per gli utenti che devono imparare di più per usare la lingua . Qualcosa come Java semplice e facile da usare diventa C ++ complesso e difficile da imparare , cose del genere. I membri della mailing list potrebbero alludere alle critiche alla precedente "grande ondata di miglioramenti", un esempio di rilievo è Java 5 che fa schifo l' articolo di Clinton Begin di MyBatis
moscerino

3
"Semplice e facile da usare Java diventa un C ++ complesso e difficile da imparare": Sfortunatamente, un problema con i linguaggi di programmazione tradizionali è che devono mantenere la compatibilità con le versioni precedenti e quindi tendono a diventare troppo complessi. Quindi IMHO (avendo molti anni di esperienza con C ++ e Java) c'è qualcosa di vero in questa affermazione. Ho spesso la sensazione che Java dovrebbe essere congelato e invece dovrebbe essere sviluppato un nuovo linguaggio. Ma Oracle non può correre questo rischio.
Giorgio,

4
@edalorzo: come hanno dimostrato Clojure, Groovy, Scala e altre lingue, è possibile (1) definire un nuovo linguaggio (2) utilizzare tutta la ricchezza di librerie e strumenti già scritti in Java senza interrompere la retrocompatibilità (3) I programmatori Java hanno la libertà di scegliere se rimanere con Java, passare a un'altra lingua o utilizzare entrambi. IMO, l'espansione indefinita di una lingua esistente è una strategia per mantenere i clienti, allo stesso modo in cui le aziende di telefonia mobile devono creare sempre nuovi modelli.
Giorgio

2
Come ho capito da SAMbdas in Java , uno dei motivi è che sarebbe problematico mantenere alcune librerie (collezioni) retrocompatibili.
Petr Pudlák, l'

2
Post correlato su SO che collega a questo altro post di Goetz .
assylias

Risposte:


6

L'approccio SAM è in realtà un po 'simile a quello che Scala (e C ++ 11) fanno con le funzioni anonime (create con l' =>operatore Scala o la []()sintassi di C ++ 11 (lambda)).

La prima domanda a cui rispondere sul lato Java è se il tipo di ritorno di un'istruzione lambda deve essere un nuovo tipo di primitve, come into byte, oppure un tipo di oggetto di qualche tipo. In Scala, non sono presenti tipi primitivi - anche un intero è un oggetto della classe Int- e funzioni sono differenti, in quanto oggetti di classe Function1, Function2e così via, a seconda del numero di argomenti della funzione prende.

C ++ 11, ruby ​​e python hanno allo stesso modo espressioni lambda che restituiscono un oggetto richiamabile in modo esplicito o implicito. L'oggetto restituito ha un metodo standard (come quello #callche può essere usato per chiamarlo come una funzione. C ++ 11, per esempio, usa un std::functiontipo che si sovraccarica in operator()modo che le chiamate del metodo di chiamata dell'oggetto assomiglino anche alle chiamate di funzione, testualmente. :-)

Laddove la nuova proposta per Java diventa disordinata è l'uso della tipizzazione strutturale per consentire l'assegnazione di tale metodo a un altro oggetto, come ad esempio uno Comparatorche ha un singolo metodo principale con un nome diverso . Mentre questo è concettualmente icky in qualche modo, fa medio che l'oggetto risultante può essere passata alle funzioni esistenti che prendono un oggetto per rappresentare un callback, un comparatore, e così via, e si aspettano di essere in grado di chiamare un singolo ben definito metodo come #compareo #callback. Il trucco di C ++ di scavalcare operator()ordinatamente evitava questo problema anche prima di C ++ 11, poiché tutte queste opzioni di callback erano richiamabili allo stesso modo, e quindi l'STL non richiedeva alcuna regolazione per consentire l'uso di lambda C ++ 11 consorte così via. Java, non avendo usato una denominazione standard per tali oggetti in passato (forse perché nessun trucco come il sovraccarico dell'operatore ha reso evidente un singolo approccio), non è così fortunato, quindi questo hack impedisce loro di cambiare molte API esistenti .


1
Ironia della sorte, il problema se avere una miriade di tipi diversi e incompatibili, tutti che rappresentano un qualche tipo di "cosa simile a una funzione", avrebbe potuto evitarsi ordinatamente aggiungendo presto tipi di funzione standard alla libreria, indipendentemente dall'avere una sintassi letterale effettiva per costruirli. Se ci fosse stato un java.util.Function2<T1, T2, R>in Java 1.0, allora non ci sarebbe alcuna Comparatorinterfaccia, invece tutti quei metodi prenderebbero un Function2<T1, T2, int>. (Bene, ci sarebbe un'intera striscia di interfacce come a Function2TT_int<T1, T2>causa dei primitivi, ma ottieni il mio punto.)
Jörg W Mittag
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.