In che modo le lingue imperative sono più diverse l'una dall'altra rispetto alle lingue funzionali?


17

Sto leggendo L'implementazione dei linguaggi di programmazione funzionale di Simon Peyton Jones e c'è un'affermazione che mi ha sorpreso un po '(a pagina 39):

In misura molto maggiore rispetto alle lingue imperative, le lingue funzionali sono in gran parte variazioni sintattiche l'una dell'altra, con relativamente poche differenze semantiche.

Ora, questo è stato scritto nel 1987 e i miei pensieri su questo argomento potrebbero essere influenzati da linguaggi di programmazione più moderni che non erano in circolazione o popolari allora. Tuttavia, lo trovo un po 'difficile da credere. Ad esempio, penso che il linguaggio di programmazione Miranda descritto (uno dei primi predecessori di Haskell) abbia una semantica molto più diversa rispetto a un linguaggio rigoroso come ML che dire che C deve parlare a Pascal o forse anche C deve parlare a parole piccole (anche se cedo che Il C ++ fornisce qualche conferma del suo punto :-).

Ma poi di nuovo, lo sto basando sulla mia comprensione intuitiva. Simon Peyton Jones è in gran parte corretto nel dire questo, o è un punto controverso?

Risposte:


19

Simon ha sostanzialmente ragione, da un punto di vista estensivo. Sappiamo abbastanza bene quali sono le semantiche dei moderni linguaggi funzionali, e in realtà sono variazioni relativamente piccole l'una dell'altra - ognuna di esse rappresenta traduzioni leggermente diverse in un metalinguaggio monadico. Perfino un linguaggio come Scheme (un linguaggio imperativo di ordine superiore tipizzato in modo dinamico con controllo di prima classe) ha una semantica che è abbastanza vicina a quella di ML e di Haskell.

Da un punto di vista denotazionale, si può iniziare dando un'equazione di dominio piuttosto semplice per la semantica di Scheme - lo chiamano . Alla fine degli anni '70 / primi anni '80, le persone potevano risolvere e risolvere equazioni come queste, quindi non è poi così male. Allo stesso modo, ci sono semantiche operative relativamente semplici anche per Scheme. (Nota che quando dico "Schema", intendo un calcolo lambda non tipizzato più continuazioni più stato, al contrario di Schema reale che ha alcune verruche come fanno tutte le lingue reali.)V

Ma per arrivare a una categoria adatta all'interpretazione dei linguaggi funzionali tipizzati moderni, le cose diventano piuttosto spaventose. Fondamentalmente, si finisce per costruire una categoria ultrametrica arricchita di relazioni di equivalenza parziali su questo dominio. (Ad esempio, vedi "Semantica di realizzabilità del polimorfismo parametrico, riferimenti generali e tipi ricorsivi" di Birkedal, Stovring e Thamsborg.) Le persone che preferiscono la semantica operativa conoscono queste cose come relazioni logiche indicizzate a passi. (Ad esempio, vedi "Indipendenza della rappresentanza dipendente dallo stato" di Ahmed, Dreyer e Rossberg.) Ad ogni modo, le tecniche utilizzate sono relativamente nuove.

La ragione di questa complessità matematica è che dobbiamo essere in grado di interpretare contemporaneamente il polimorfismo parametrico e lo stato di ordine superiore. Ma una volta che hai fatto questo, sei praticamente a casa libera, poiché questa costruzione contiene tutti i bit duri. Ora puoi interpretare i tipi ML e Haskell tramite le solite traduzioni monadiche. Lo spazio funzionale rigoroso ed efficace di ML si a -> btraduce in <a right> e lo spazio delle funzioni pigro di Haskell si traduce in <a right> , con il tipo monadico di effetti collaterali che interpretano la monade IO di Haskell e è l'interpretazione del tipo ML o del tipo Haskell eun'TBun'BT(UN)un'a è l'esponenziale in quella categoria di PER.

Per quanto riguarda la teoria equazionale, poiché entrambe queste lingue possono essere descritte da traduzioni in sottoinsiemi leggermente diversi della stessa lingua, è del tutto corretto chiamarle variazioni sintattiche l'una dell'altra.

La differenza di feeling tra ML e Haskell deriva in realtà dalle proprietà intensionali delle due lingue, ovvero tempo di esecuzione e consumo di memoria. ML ha un modello di performance compositiva (vale a dire, il costo tempo / spazio di un programma può essere calcolato dal costo tempo / spazio dei suoi subterm), come farebbe un vero linguaggio call-by-name. L'attuale Haskell è implementato con una chiamata per necessità, una sorta di memoizzazione e, di conseguenza, le sue prestazioni non sono composizionali - il tempo impiegato da un'espressione legata a una variabile per valutare dipende dal fatto che sia stato usato prima o meno. Questo non è modellato nella semantica cui ho accennato in precedenza.

Se vuoi prendere le proprietà intensionali più seriamente, allora ML e Haskell iniziano a mostrare differenze più serie. Probabilmente è ancora possibile escogitare un metalinguaggio comune per loro, ma l'interpretazione dei tipi differirà in un modo molto più sistematico, correlato all'idea teorica di messa a fuoco . Un buon posto per conoscere questo è la tesi di dottorato di Noam Zeilberger.


10

La mia sensazione è che SPJ si riferisse a linguaggi puramente funzionali - cioè linguaggi che sono referenzialmente trasparenti. Ciò include, ad esempio, Haskell, Miranda, Clean, ma non ML. Una volta che hai un linguaggio puramente funzionale, in generale, puoi dargli una semantica denotazionale abbastanza chiara e ben definita. Questa semantica, in generale, sembrerà una per il calcolo lambda, con alcune modifiche qua e là. Generalmente, avrai un sistema di tipo che cerca di somigliare a una variante del Sistema F - forse più potente per alcuni aspetti, più limitato in altri. Questo è il motivo per cui l'estrazione / compilazione del codice in Haskell, O'Caml, ecc. È relativamente semplice da sofisticati assistenti di correzione tipicamente dipendenti come Agda.

All'interno di quel quadro, c'è molto spazio per giocare. Certamente, c'è ancora una differenza tra un linguaggio non rigido e un linguaggio rigoroso. Tuttavia, in assenza di effetti collaterali, l'unica differenza è che un linguaggio non rigoroso contiene più espressioni che non indicano il fondo - nella misura in cui entrambe le strategie di valutazione non danno il fondo, concordano.

La dichiarazione di Simon si inserisce anche in un contesto storico molto importante. Al tempo della nascita di Haskell (1987), c'era una panoplia di linguaggi funzionali non rigidi - non solo Miranda, ma Lazy ML, Orwell, Clean e molti altri. A parte alcune variazioni sintattiche, erano tutte nella stessa lingua. Quale fu precisamente la motivazione per la formazione del Comitato Haskell. Per ulteriori informazioni, vedi "Una storia di Haskell: essere pigri con la classe": http://research.microsoft.com/en-us/um/people/simonpj/papers/history-of-haskell/ .


5

Penso che SPJ abbia ragione nel dire questo per la semantica di base.

Mentre ci sono molte sottigliezze avanzate a cui si può puntare, come il default alla valutazione rigorosa o pigra, come dici tu, i dettagli dei sistemi di tipo o l'organizzazione di unità di codice più grandi (moduli, strutture), il modello mentale di un programma è molto simile in tutti i linguaggi funzionali.

Scegli una determinata funzione specifica e scrivila in tutte le lingue che stai confrontando, e probabilmente scoprirai che la struttura e la semantica delle diverse implementazioni saranno molto simili per tutte, incluso il livello di astrazione, le strutture di dati scelte, loro ' Tutti presumiamo la raccolta dei rifiuti, ecc.

Al contrario, supponiamo che un'implementazione C rispetto a un'implementazione Smalltalk della stessa funzione avrà probabilmente una struttura diversa (funzioni e strutture dati di basso livello rispetto agli oggetti), concentrandosi su diversi livelli di dettaglio (ad esempio, gestione manuale della memoria vs garbage collection ) e operare a diversi livelli di astrazione.

La visione del mondo dello spazio di progettazione del linguaggio funzionale è semplicemente più specifica e coerente di quella dello spazio di "programmazione imperativa" che raggruppa assembly, C, Smalltalk, Forth e dozzine di altri linguaggi radicalmente diversi in una categoria catchall.


4

Penso che la citazione di Simon PJ sia in realtà un po 'un'osservazione fuori mano.

La somiglianza tra le lingue è determinata dal consenso che è stato generato nella comunità di ricercatori e designer di lingue. Non vi è dubbio che vi sia un maggior grado di consenso nella comunità di programmazione funzionale rispetto alla comunità di programmazione imperativa. Ma è anche vero che i linguaggi di programmazione funzionale sono progettati principalmente dai ricercatori piuttosto che dai professionisti. Quindi, è naturale che emerga tale consenso.

Quasi tutti i linguaggi funzionali utilizzano la gestione della memoria raccolta rifiuti e strutture di dati ricorsive (originate da Lisp), la maggior parte di essi utilizza tipi di dati "algebrici" e adattamento dei modelli (originati da Hope), molti usano funzioni di ordine superiore e funzioni polimorfiche ( originato da ML). Oltre a ciò, il consenso scompare. Differiscono nei sistemi di moduli utilizzati, come devono essere gestite le operazioni di cambio di stato e altri effetti computazionali e l'ordine di valutazione (chiamata per nome vs chiamata per valore) ecc.

I linguaggi di programmazione imperativa generalmente utilizzano strutture di controllo nidificate (originate da Algol 60) e sistemi di tipi (originati da Algol 60, ma consolidati da Algol 68). Generalmente hanno una sintassi ingombrante (che risale ancora ad Algol 60), fanno tentativi incerti di gestire funzioni di ordine superiore e tipi polimorfici e differiscono nel loro supporto per la struttura a blocchi e i sistemi di moduli. Probabilmente c'è più uniformità nell'ordine di valutazione perché, dopo gli anni '60, la chiamata per nome è sostanzialmente scomparsa dalle lingue imperative.

Quindi, non mi è chiaro che la differenza tra le due classi di lingue nella loro uniformità sia così grande.

Sarebbe davvero utile introdurre le notazioni più pulite e uniformi della programmazione funzionale in linguaggi di programmazione imperativi. Vedo che Scala ha iniziato in quella direzione. Resta da vedere se la tendenza continuerà.


Mi chiedo pigramente perché hai usato le virgolette nei tipi di dati "algebrici"?
Steven Shaw,
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.