Perché è bene non fare affidamento sul cambiamento di stato?


16

Questa domanda nasce dalla domanda /software/25569/is-haskell-worth-learning

Generalmente vengono fatte alcune affermazioni spesso ripetute su come Haskell migliora le tue capacità di programmazione in altre lingue e, inoltre, questo è perché Haskell è apolide, ed è una buona cosa.

Perché?

Ho visto qualcuno paragonarlo al solo digitare con la mano sinistra, o forse chiudere gli occhi per un giorno e fare affidamento solo sul tocco. Sicuramente c'è di più?

Riguarda l'accesso alla memoria hardware o qualcos'altro che è un grande guadagno in termini di prestazioni?


2
Haskell è accademico. Vorrei guardare alcune lezioni di Rich Hickey su Clojure - lì fa delle argomentazioni pragmatiche assassine (simili ai 3 punti di Javier, ma anche fatte in modo semplice).
Giobbe


6
Solo perché Haskell è "accademico" non significa che sia poco pratico o non pragmatico.
Tikhon Jelvis,

Risposte:


17

ci sono almeno tre grandi vantaggi dalla cima della mia testa:

  1. rende i programmi più vicini alle espressioni matematiche. In matematica, xnon cambia, semplicemente non sai di cosa si tratta fino a quando non risolvi l'equazione.

  2. Alla fine, non v'è il cambiamento di stato (dopo tutto, è così che il computer funziona a livello basso); ma è limitato dalla lingua in luoghi specifici. ciò consente al compilatore enormi opportunità di spostare il codice per ottimizzarlo, poiché sa che non cambia nulla da cui dipende altro codice.

  3. Il codice simultaneo non deve essere sincronizzato per accedere ai dati non modificabili, quindi la concorrenza è migliorata, sia nei sistemi di memoria condivisa SMP (tutti i sistemi multicore di oggi), sia nei cluster liberamente collegati.


3
I punti di forza della programmazione funzionale pongono la maggior parte dello stress n. 3, vale a dire una più semplice ripartizione della concorrenza.
Mchl

4
@Mchl: Nella mia esperienza hanno messo maggiormente l'accento su "È più facile capire e ragionare su", che tipo di corrisponde a 1. Anche se suppongo che potrebbe differire tra le comunità linguistiche.
sepp2k,

+1, risposta molto completa. @ sepp2k: immagino siano entrambi importanti. Ragionare su un programma è ciò che facciamo quotidianamente, ed è vero che se non devi controllare che lo stato non sia cambiato in alcune funzioni profondamente nascoste è molto più facile leggere solo metodi di alto livello e capire cosa sta succedendo. Dal punto di vista dell'hardware: dal momento che ci stiamo muovendo sempre più verso multi-core e multi-processori (anche se immagino che passerà un po 'di tempo prima che i computer domestici ottengano multi-processori), avere un linguaggio che faciliti la programmazione concorrente è un premio.
Matthieu M.

Buona risposta, n. 2 era quella che presumevo fosse la risposta, e sebbene abbia letto spesso dei vantaggi del multiprocessing, raramente viene dichiarato chiaramente, ottimo lavoro.
ottobre

1
@Yttrill, i linguaggi funzionali non sono univoci nella loro dipendenza dalla garbage collection e la garbage collection è molto più semplice quando si ha a che fare con dati immutabili. Fare qualsiasi calcolo su un'architettura basata su istruzioni significa modificare lo stato, ciò non significa che i linguaggi funzionali siano in qualche modo più difficili da parallelizzare. I linguaggi funzionali oscillano al parallelismo; ricerca dati paralleli Haskell, è una caratteristica che sarebbe quasi impossibile aggiungere a un linguaggio imperativo.
dan_waterworth

4

Ecco un altro vantaggio: accoppiamento ridotto. Se hai un codice come:

 function doStuff(x) { return x + y;}

e altrove hai:

 function doOtherStuff(x) { y++; return y + x;}

quindi le due funzioni sono implicitamente dipendenti . Non esiste un modo semplice per dire che la chiamata doStuffè influenzata dalla chiamata doOtherStuff. Senza lo stato mutabile, dovresti rendere esplicita la connessione.

Naturalmente, questo non è un problema con tutti gli stati mutabili - il problema è con uno stato mutevole pervasivo. La vera soluzione è quella di avere l'immutabilità di default e un modo per "contrassegnare" e limitare lo stato mutevole proprio dove ne hai bisogno.


+1. Molti programmatori esperti sanno di non scrivere codice come sopra e non è un grande passo da "stato mutevole in questa situazione" a "riduciamo seriamente la quantità di stato mutabile e scriviamo in modo più funzionale", ma è un passo che in modo deludente pochi fanno.
dan_waterworth,

2

Una risposta semplificata è: quando vedi un nome in un linguaggio puramente funzionale, sai qual è il valore associato da una semplice ricerca della sua definizione. Se disponi di variabili mutabili, puoi solo sapere da quale delle varie assegnazioni è stata eseguita l'ultima volta, quindi devi anche analizzare il flusso di controllo, che a sua volta potrebbe essere condizionale, lasciandoti con più possibilità. Per ottenere un'esplosione esponenziale devi solo considerare che l'RHS degli incarichi dipende da loro stessi dalle variabili, quindi devi analizzarli in modo ricorsivo.

La linea di fondo nell'analisi sopra è che è insostenibile senza commenti che spiegano l'intento, gli invarianti e la semantica: questi possono essere difficili da interpretare e può essere difficile verificare che la semantica sia rispettata nel codice reale.

Questa risposta è sostanzialmente un'espansione del punto 1 di @ Javier.

Penso che sia anche una spiegazione della popolarità del regime fraudolento di OO: con OO si incapsula lo stato mutevole che rende l'analisi molto più semplice localizzando le mutazioni in una certa misura e permettendo un'espressione e una verifica molto più robuste della semantica.

Detto questo, la programmazione funzionale non è la risposta. La risposta giusta è un sistema che supporta la programmazione induttiva (funzionale) e coinduttiva (procedurale), quindi gli strumenti giusti possono gestire sia la programmazione senza stato che quella con stato. È solo che la teoria costruttiva (funzionale) è ben consolidata mentre la teoria della gestione dello stato è ancora agli inizi.


Mescolare la programmazione funzionale e imperativa è esattamente ciò che fa Haskell: le normali funzioni sono funzionali mentre i calcoli con stato possono essere espressi con notazione che consente di isolare e controllare attentamente lo stato mutabile (tra le altre cose). Ecco perché il linguaggio con l'implementazione STM più pratica è Haskell .
Tikhon Jelvis,

La notazione non ha davvero nulla da fare per il calcolo con stato di per sé . La notazione è solo una sintassi più semplice in cima alle pipeline di funzioni monadiche. Il calcolo con stato è semplicemente una delle cose che possono essere espresse usando le monadi, e quindi la notazione.
Jonathan Sterling

La programmazione mista e quella imperativa sono ciò che fanno quasi tutte le lingue esistenti. Haskell potrebbe aver fornito un modo per isolare le parti stateful, ma ciò non lo rende un mix adeguato: la carità è più simile a come dovrebbe essere (IMHO).
Yttrill

2

Come autore di Siege , un DBMS scritto in Haskell, alcuni potrebbero definire in conflitto il mio punto di vista sullo stato mutabile. Spero di mostrare il contrario.

Lo scopo dello stato mutevole è descrivere lo stato corrente in cui si trova un sistema. Supponiamo che tu abbia un blog ed è back-end da un database, il database descrive i post che hai sul tuo blog nel momento in cui richiedi esso. Quanti post esistono adesso?

Contrastare questo con lo stato immutabile che viene utilizzato per trasmettere fatti. Quanti post c'erano il 12 agosto?

I fatti sono facili da ragionare, lo stato mutevole no. Tuttavia, lo stato mutevole non è un effetto malvagio impuro che dovrebbe essere bandito dalle nostre menti; spesso ne abbiamo bisogno per coesistere nel mondo mutevole in cui viviamo, dobbiamo solo usarlo con più parsimonia.

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.