Perché utilizziamo strutture di dati persistenti nella programmazione funzionale?


22

La programmazione funzionale impiega strutture di dati persistenti e oggetti immutabili. La mia domanda è: perché è fondamentale disporre di tali strutture di dati qui? Voglio capire a basso livello cosa accadrebbe se la struttura dei dati non fosse persistente? Il programma andrebbe in crash più spesso?


c'è una discussione abbastanza estesa di questo in abelson & sussman, struttura e interpretazione dei programmi per computer
vzn

Risposte:


19

Quando si lavora con oggetti dati immutabili, le funzioni hanno la proprietà che ogni volta che li si chiama con gli stessi input, producono gli stessi output. Ciò semplifica la concettualizzazione dei calcoli e la corretta. Li rende anche più facili da testare.

Questo è solo un inizio. Poiché la matematica ha a lungo lavorato con le funzioni, ci sono molte tecniche di ragionamento che possiamo prendere in prestito dalla matematica e usarle per ragionamenti rigorosi sui programmi. Il vantaggio più importante dal mio punto di vista è che i sistemi di tipi per i programmi funzionali sono ben sviluppati. Quindi, se commetti un errore da qualche parte, le probabilità sono molto alte che si manifesti come una mancata corrispondenza del tipo. Quindi, i programmi funzionali digitati tendono ad essere molto più affidabili dei programmi imperativi.

Quando si lavora con oggetti dati mutabili, al contrario, si ha prima il carico cognitivo di ricordare e gestire i molteplici stati che l'oggetto attraversa durante un calcolo. Devi fare attenzione a fare le cose nel giusto ordine, assicurandoti che tutte le proprietà necessarie per un determinato passaggio siano soddisfatte a quel punto. È facile commettere errori e i sistemi di tipi non sono abbastanza potenti da intercettarli.

La matematica non ha mai funzionato con oggetti dati mutabili. Quindi, non ci sono tecniche di ragionamento che possiamo prendere in prestito da loro. Ci sono molte nostre tecniche sviluppate in Informatica, in particolare Logica Floyd-Hoare . Tuttavia, queste sono più difficili da padroneggiare rispetto alle tecniche matematiche standard, la maggior parte degli studenti non è in grado di gestirle, quindi raramente vengono insegnate.

Per una rapida panoramica di come i due paradigmi differiscono, potresti consultare le prime dispense delle mie lezioni su Principi dei linguaggi di programmazione .


Questo ha molto senso per me. Grazie per aver condiviso i tuoi PPT. Condividete anche registrazioni video delle stesse?
gpuguy,

@gpuguy. Non uso così tanto PowerPoint. La lavagna è il mio mezzo preferito. Ma i volantini dovrebbero essere abbastanza leggibili da soli.
Uday Reddy,

+1 La matematica non ha mai funzionato con oggetti dati mutabili. Anche il link alle note della lezione.
Guy Coder,

15

È più facile lavorare correttamente con strutture di dati persistenti che lavorare con strutture di dati mutabili. Questo, direi, è il vantaggio principale.

Naturalmente, teoricamente parlando, qualsiasi cosa facciamo con strutture di dati persistenti, possiamo anche fare con strutture mutabili e viceversa. In molti casi le strutture di dati persistenti comportano costi aggiuntivi, in genere perché è necessario copiarne parti. Queste considerazioni avrebbero reso le strutture di dati persistenti molto meno attraenti 30 anni fa quando i supercomputer avevano meno memoria del tuo cellulare. Ma oggi i principali colli di bottiglia nella produzione di software sembrano essere i tempi di sviluppo e i costi di manutenzione. Pertanto, siamo disposti a sacrificare un po 'di efficienza nell'esecuzione per efficienza nello sviluppo.

Perché è più facile usare strutture di dati persistenti? Perché gli umani sono davvero pessimi nel tracciare l' aliasing e altri tipi di interazioni impreviste tra le diverse parti di un programma. Lo pensano automaticamente perché due cose vengono chiamate xe y, quindi, non hanno nulla in comune. Dopo tutto, ci vuole sforzo per capire che "la stella del mattino" e "la stella della sera" sono davvero la stessa cosa. Allo stesso modo, è molto facile dimenticare che una struttura di dati può cambiare perché altri thread ci stanno lavorando o perché abbiamo chiamato un metodo che cambia la struttura dei dati, ecc. Molte di queste preoccupazioni non sono presenti quando lavoriamo con strutture di dati persistenti.

Strutture di dati persistenti presentano anche altri vantaggi tecnici. In genere è più facile ottimizzarli. Ad esempio, sei sempre libero di copiare una struttura di dati persistente su qualche altro nodo nel tuo cloud, se lo desideri, non c'è preoccupazione per la sincronizzazione.


quando ha così tanti vantaggi, allora perché non usare una struttura di dati persistente anche in linguaggi imperativi?
gpuguy,

4
Forse presto ti chiederai "Perché usare le lingue imperative?"
Andrej Bauer,

4
Ma seriamente, ci sono strutture di dati che sono difficili da sostituire con quelle persistenti, ad esempio i programmi di analisi del numero che usano array e matrici sono molto più veloci con le strutture di dati tradizionali perché l'hardware è ottimizzato per quel tipo di cose.
Andrej Bauer,

1
@gpuguy. Strutture di dati persistenti possono essere e dovrebbero essere utilizzate anche in linguaggi imperativi, ogni volta che sono applicabili e idonee. Per poterli utilizzare, il linguaggio dovrebbe supportare la gestione della memoria basata sulla raccolta dei rifiuti. Molte lingue moderne lo hanno: Java, C #, Scala, Python, Ruby, Javascript ecc.
Uday Reddy,

Probabilmente, un grande vantaggio è l'interfaccia più astratta rispetto alle strutture di dati mutabili. È possibile modificare roba sotto il cofano (cfr immutabilità vs integrità refential), ma non è necessario.
Raffaello

2

Aggiungendo alle risposte degli altri e rafforzando un approccio matematico, la programmazione funzionale ha anche una bella sinergia con Algebra relazionale e Galois Connections.

Ciò è estremamente utile nell'area dei metodi formali.
Per esempio:

  • Le prove formali nella verifica del programma sono semplificate con il controllo statico esteso;
  • Numerose proprietà di Algebra relazionale sono utili nella risoluzione di SAT, con strumenti come Alloy;
  • Galois Connections consente un approccio di calcolo alle specifiche del software, come visto in questo blog , con riferimento a un articolo , di Shin-Cheng Mu e José Nuno Oliveira.
  • Galois Connections (e la programmazione funzionale) possono essere utilizzati in modo Design by Contract, poiché sono un concetto più generale di Hoare Logic.

Esempio

{p}P{q}[P]φpφq[P]

  • [P]P
  • φpφq)pq

Questo approccio consente anche il calcolo delle pre-condizioni più deboli e delle post-condizioni più forti , utile in diverse situazioni.


2

Voglio capire a basso livello cosa accadrebbe se la struttura dei dati non fosse persistente?

Diamo un'occhiata a un generatore di numeri pseudocasuali con un enorme spazio degli stati (come " Mersenne twister " con uno stato di 2450 byte) come una struttura di dati. Non vogliamo davvero usare alcun numero casuale più di una volta, quindi sembrano esserci poche ragioni per implementarlo come una struttura di dati persistente immutabile. Ora chiediamoci cosa potrebbe andare storto nel seguente codice:

mt_gen = CreateMersenneTwisterPRNGen(seed)
integral = MonteCarloIntegral_Bulk(mt_gen) + MonteCarloIntegral_Boundary(mt_gen)

La maggior parte dei linguaggi di programmazione non specifica l'ordine in cui MonteCarloIntegral_BulkeMonteCarloIntegral_Boundary sarà valutato. Se entrambi prendono un riferimento a un mt_gen mutabile come argomento, il risultato di questo calcolo può dipendere dalla piattaforma. Peggio ancora, potrebbero esserci piattaforme in cui il risultato non è affatto riproducibile tra diverse esecuzioni.

Si può progettare un'efficiente struttura di dati mutabili per mt_gen in modo tale che qualsiasi interleaving dell'esecuzione di MonteCarloIntegral_BulkeMonteCarloIntegral_Boundary fornisca un risultato "corretto", ma un diverso interleaving porterà in generale a un diverso risultato "corretto". Questa non riproducibilità rende la funzione corrispondente "impura" e comporta anche altri problemi.

La non riproducibilità può essere evitata applicando un ordine di esecuzione sequenziale fisso. Ma in quel caso il codice potrebbe essere organizzato in modo tale che sia disponibile un solo riferimento a mt_gen in qualsiasi momento. In un linguaggio di programmazione funzionale tipizzato, i tipi di unicità potrebbero essere utilizzati per applicare questo vincolo, consentendo in tal modo aggiornamenti sicuri mutabili anche nel contesto di linguaggi di programmazione funzionale pura. Tutto ciò può sembrare bello e dandy, ma almeno in teoria lo sono le simulazioni Monte Carlo imbarazzanti parallelamentee la nostra "soluzione" ha appena distrutto questa proprietà. Questo non è solo un problema teorico, ma un problema pratico molto reale. Tuttavia, dobbiamo modificare (la funzionalità offerta da) il nostro generatore di numeri pseudocasuali e la sequenza di numeri casuali che produce, e nessun linguaggio di programmazione può farlo automaticamente per noi. (Ovviamente possiamo usare una diversa libreria di numeri pseudocasuali che offre già la funzionalità richiesta.)

A un livello basso, le strutture di dati mutabili portano facilmente alla non riproducibilità (e quindi all'impurità), se l'ordine di esecuzione non è sequenziale e fisso. Una tipica strategia imperativa per affrontare questi problemi è di avere fasi sequenziali con ordine di esecuzione fisso, durante il quale le strutture di dati mutabili vengono modificate e fasi parallele con ordine di esecuzione arbitrario, durante le quali tutte le strutture di dati mutabili condivise rimangono costanti.


Andrej Bauer ha sollevato il problema dell'aliasing per le strutture di dati mutabili. È interessante notare che diversi linguaggi imperativi come Fortran e C hanno ipotesi diverse sull'aliasing consentito degli argomenti di funzione e la maggior parte dei programmatori non è del tutto consapevole del fatto che il loro linguaggio abbia un modello di aliasing.

L'immutabilità e la semantica del valore potrebbero essere leggermente sopravvalutate. La cosa più importante è che il sistema di tipi e il framework logico (come il modello di macchina astratto, il modello di aliasing, il modello di concorrenza o il modello di gestione della memoria) del tuo linguaggio di programmazione offrono supporto sufficiente per lavorare "in sicurezza" con dati "efficienti" strutture. L'introduzione di "spostare la semantica" in C ++ 11 potrebbe sembrare un gigantesco passo indietro in termini di purezza e "sicurezza" da un punto di vista teorico, ma in pratica è l'opposto. Il sistema dei tipi e la struttura logica del linguaggio sono stati estesi per rimuovere enormi parti del pericolo associato alla nuova semantica. (E anche se i bordi grezzi rimangono, ciò non significa che questo non possa essere migliorato da un "migliore"


Uday Reddy ha sollevato il problema che la matematica non ha mai funzionato con oggetti di dati mutabili e che i sistemi di tipi per i programmi funzionali sono ben sviluppati per oggetti di dati immutabili. Questo mi ha ricordato la spiegazione di Jean-Yves Girard secondo cui la matematica non viene utilizzata per lavorare con oggetti mutevoli, quando cerca di motivare la logica lineare.

Ci si potrebbe chiedere come estendere il sistema di tipi e la struttura logica dei linguaggi di programmazione funzionale per consentire di lavorare "in sicurezza" con strutture di dati non persistenti mutevoli "efficienti". Un problema qui potrebbe essere che la logica classica e le algebre booleane potrebbero non essere il miglior framework logico per lavorare con strutture di dati mutabili. Forse la logica lineare e i monoidi commutativi potrebbero essere più adatti a questo compito? Forse dovrei leggere ciò che Philip Wadler ha da dire sulla logica lineare come sistema di tipi per linguaggi di programmazione funzionale? Ma anche se la logica lineare non dovrebbe essere in grado di risolvere questo problema, ciò non significa che il sistema dei tipi e il framework logico di un linguaggio di programmazione funzionale non possano essere estesi per consentire "sicuri" ed "efficienti"


@DW Probabilmente hai ragione nel dire che questa risposta non è una risposta autonoma. Attualmente si estende solo su alcuni punti sollevati nelle risposte di Uday Reddy e Andrej Bauer. Penso di poterlo modificare per essere autonomo e rispondere direttamente a "Voglio capire a basso livello cosa accadrebbe se la struttura dei dati non fosse persistente?" parte della domanda. Vorrei guardare un generatore di numeri pseudocasuali con un enorme spazio di stato (come "twister di Mersenne" con stato di 2450 byte) come una struttura di dati e spiegare cose che possono andare storte.
Thomas Klimpel,

@DW Non credo che nessuna delle risposte a questa domanda risponda davvero alla domanda. In particolare, non c'è molto su cosa siano realmente le strutture di dati persistenti (oltre a essere immutabili) e su come vengono implementate.
Guildenstern,
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.