I linguaggi impuri non differiscono in linea di principio dai linguaggi imperativi più familiari, specialmente ora che sono stati copiati molti trucchi funzionali. Ciò che è diverso è lo stile: come risolvi i problemi.
Che contenga Haskell come puro o che contenga la monade IO come impurità, lo stile Haskell è una forma estrema di questo stile e vale la pena di essere studiato.
La monade Haskell IO è derivata dalla teoria matematica delle (ovviamente) monadi. Tuttavia, per i programmatori imperativi, penso che un modo arretrato di arrivare alle monadi abbia più senso.
Fase uno: un linguaggio puramente funzionale può facilmente restituire un valore di stringa grande come risultato. Questa grande stringa può essere il codice sorgente di un programma imperativo, derivato in modo puramente funzionale da alcuni parametri che specificano i requisiti. È quindi possibile creare un compilatore "di livello superiore" che esegue il generatore di codice, quindi alimenta automaticamente il codice generato nel compilatore del linguaggio imperativo.
Fase due: anziché generare un codice sorgente testuale, si genera un albero di sintassi astratto strongmente tipizzato. 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 distinti tipi 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, rendi i due uguali: la stessa funzione con la stessa sintassi può essere parzialmente valutata durante la "generazione del codice", o completamente valutata, o non valutata affatto. Inoltre, eliminare tutti i nodi AST del costrutto di ciclo a favore della ricorsione. In effetti, scartare l'idea dei nodi AST come un tipo speciale di dati del tutto - non hanno nodi AST "valore letterale", ma solo valori ecc.
Questo è praticamente ciò che fa il 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 all'ora di esecuzione, non da una regola speciale, ma come conseguenza naturale delle dipendenze dei dati in espressioni.
Le Monade 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à senz'altro esplodere le teste dei puristi, ma per me spiega alcuni dei veri motivi per cui Haskell è interessante. Sfoca il confine tra programmazione e metaprogrammazione e utilizza gli strumenti della programmazione funzionale per reinventare la programmazione imperativa senza richiedere una sintassi speciale.
Una critica che ho dei template C ++ è che sono una specie di sublocazione funzionale pura in un linguaggio imperativo - per valutare la stessa funzione di base in fase di compilazione piuttosto che in fase di esecuzione devi ri-implementarla usando un stile di codifica completamente diverso. 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 che in senso run-time non-meta-programmazione nello stesso programma - non c'è una linea dura tra programmazione e metaprogrammazione.
Detto questo, ci sono alcune cose che metaprogrammano che Haskell standard non può fare, fondamentalmente perché i tipi (e forse poche altre cose) non sono valori di prima classe. Ci sono varianti linguistiche che cercano di affrontare questo, però.
Molte delle cose che ho detto su Haskell possono essere applicate in lingue funzionali impure - e talvolta anche in lingue imperative. Haskell è diverso perché non hai altra scelta che seguire questo approccio: in pratica ti costringe a imparare questo stile di lavoro. Puoi "scrivere C in ML", ma non puoi "scrivere C in Haskell" - almeno non senza imparare cosa sta succedendo sotto il cofano.