Le lingue impure non differiscono in linea di principio dalle lingue imperative più familiari, soprattutto ora che sono stati copiati molti trucchi funzionali. Ciò che differenzia è lo stile: come risolvi i problemi.
Sia che tu consideri Haskell puro o che la monade IO sia impurità, lo stile Haskell è una forma estrema di questo stile e vale la pena imparare.
La monade Haskell IO è derivata dalla teoria matematica delle (ovviamente) monadi. Tuttavia, per i programmatori imperativi, penso che abbia più senso un modo arretrato di arrivare alle monadi.
Fase uno - un linguaggio funzionale puro può facilmente restituire un grande valore di stringa come risultato. Questa grande stringa può essere il codice sorgente di un programma imperativo, derivato in modo funzionale da alcuni parametri che specificano i requisiti. È quindi possibile creare un compilatore di "livello superiore" che esegue il generatore di codice, quindi inserisce automaticamente quel codice generato nel compilatore del linguaggio imperativo.
Fase due: anziché generare codice sorgente testuale, si genera un albero di sintassi astratto tipizzato fortemente. Il compilatore in linguaggio imperativo viene assorbito nel compilatore "di livello superiore" e accetta l'AST direttamente come codice sorgente. Questo è molto più vicino a quello che fa Haskell.
Questo è ancora imbarazzante, però. Ad esempio, hai due tipi distinti di funzioni: quelle valutate durante la fase di generazione del codice e quelle eseguite quando viene eseguito il programma generato. È un po 'come la distinzione tra funzioni e modelli in C ++.
Quindi, per la fase 3, rendere le due uguali: la stessa funzione con la stessa sintassi può essere parzialmente valutata durante la "generazione del codice", oppure valutata completamente o non valutata affatto. Inoltre, scartare tutti i nodi AST dei costrutti di looping a favore della ricorsione. In effetti, scarta l'idea dei nodi AST come un tipo speciale di dati del tutto - non hanno nodi AST "valore letterale", solo valori ecc.
Questo è praticamente ciò che fa la monade IO: l'operatore di bind è un modo di comporre "azioni" per formare programmi. Non è niente di speciale - solo una funzione. Molte espressioni e funzioni possono essere valutate durante la "generazione del codice", ma quelle che dipendono dagli effetti collaterali I / O devono avere una valutazione ritardata fino al runtime - non da una regola speciale, ma come conseguenza naturale delle dipendenze dei dati in espressioni.
Le monadi in generale sono solo generalizzazioni - hanno la stessa interfaccia, ma implementano le operazioni astratte in modo diverso, quindi invece di valutare una descrizione del codice imperativo valutano invece qualcos'altro. Avere la stessa interfaccia significa che ci sono alcune cose che puoi fare alle monadi senza preoccuparti di quale monade, che risulta essere utile.
Questa descrizione farà sicuramente esplodere le teste dei puristi, ma per me spiega alcune delle vere ragioni per cui Haskell è interessante. Sfoca il confine tra programmazione e metaprogrammazione e utilizza gli strumenti della programmazione funzionale per reinventare la programmazione imperativa senza la necessità di una sintassi speciale.
Una critica che ho dei modelli C ++ è che sono una sorta di sublanguage funzionale puro rotto in un linguaggio imperativo - per valutare la stessa funzione di base in fase di compilazione piuttosto che in fase di runtime, è necessario implementarla nuovamente con uno stile completamente diverso di codifica. In Haskell, mentre l'impurità deve essere etichettata come tale nel suo tipo, la stessa identica funzione può essere valutata sia in senso meta-programmazione sia in senso non-meta-programmazione runtime nello stesso programma - non esiste una linea dura tra programmazione e metaprogrammazione.
Detto questo, ci sono alcune cose di metaprogrammazione che Haskell standard non può fare, fondamentalmente perché i tipi (e forse poche altre cose) non sono valori di prima classe. Tuttavia, ci sono varianti linguistiche che tentano di risolvere questo problema.
Molte cose che ho detto su Haskell possono essere applicate in linguaggi funzionali impuri - e talvolta anche in linguaggi imperativi. Haskell è diverso perché non hai altra scelta che adottare questo approccio: ti costringe sostanzialmente a imparare questo stile di lavoro. Puoi "scrivere C in ML", ma non puoi "scrivere C in Haskell" - almeno non senza imparare cosa succede sotto il cofano.