Purezza
Uno dei concetti interessanti di Haskell è la purezza. Tuttavia, mi chiedo quali siano le ragioni pragmatiche alla base di questo - lasciami spiegare un po 'di più prima di rifiutare la mia domanda.
Il mio punto principale è che avremo effetti collaterali indipendentemente da come possiamo incapsularli in una lingua. Quindi, anche se un linguaggio di programmazione come Haskell è puro, stiamo semplicemente "posticipando" gli effetti collaterali fino a un momento successivo.
Capisco l'idea che la purezza renda più semplici i ragionamenti sui programmi (trasparenza referenziale e così via). In Haskell, incapsuliamo tutti gli effetti collaterali nel sistema di tipi (la monade IO). In quanto tale, non possiamo avere un effetto collaterale senza propagare la monade IO attraverso i tipi di tutte le funzioni che chiamano la nostra funzione impura, per così dire.
In quanto tale, sono molto interessato all'idea di separare le pure funzioni da quelle impure nel sistema dei tipi. Questo sembra un buon modo per costringerci a limitare tali effetti.
Tuttavia, Haskell viene spesso colpito a causa della sua natura pura - le persone dicono che non è pratico o pragmatico. Se questo è vero o no è un'altra questione, ma il fatto è che molte persone rifiutano Haskell per questo motivo.
Un approccio alternativo? (chiamiamolo "tipo inquinamento")
Per me, sembra che il principale punto vincente non sia la purezza in sé, ma la separazione tra codice puro e impuro. Se incapsulare IO in una monade è troppo poco pratico, perché non proviamo un approccio più semplice?
Cosa succede se invece etichettiamo ogni tipo di ritorno e tipo di argomento nel sistema come puro o impuro. Il linguaggio D ha una parola chiave "pura" esplicita, ma penso che lasciamo che le funzioni siano pure di default e segnino solo le funzioni impure.
In un linguaggio simile a ML questo potrebbe apparire come questo:
let square x = x * x
let impure getDbData connectionString = ...
let impure getMappedDbData mapping =
let dbData = getDbData "<some connection info>"
in map mapping dbData
let main = // Compiler error, as it is not marked as impure
let mappedData = getMappedData square
in ...
Inoltre, chiamare qualsiasi funzione impura "inquinerebbe" il tipo di ritorno delle funzioni di chiamata, rendendolo impuro. Se non fosse etichettato come tale, darebbe un errore di compilazione.
(naturalmente, passare le funzioni come argomenti dovrebbe essere verificato rispetto alla purezza prevista, ma dovrebbe essere facile dedurre la "purezza" prevista degli argomenti)
(anche l'etichetta "impura" potrebbe essere dedotta, suppongo che sia meglio mantenere questo esplicito, per essere leggibile)
Le funzioni pure d'altra parte possono essere trasmesse ovunque, indipendentemente dalla purezza prevista. Potremmo dare una funzione pura come input per una funzione che accetta uno impuro. Quindi non è una relazione simmetrica - quindi la parola "inquinamento".
Tutto sommato, questo esplicitamente (e controllato staticamente) separa la base di codice in una parte pura e impura. In molti modi, è vicino alla funzionalità di una monade IO (anche se l'asimmetria probabilmente esclude una mappatura 1-a-1 tra i due), ma con un involucro implicito fatto automaticamente. Ancora più importante, molti programmatori lo troverebbero più intuitivo e pragmatico di una monade IO esplicita o qualcosa di simile.
E a differenza delle soluzioni completamente impure, questo dà molta più sicurezza alla ragione riguardo alle parti pure (senza stare troppo a nostro agio nel definire quelle impure). E tutto sommato, è soprattutto una versione applicata staticamente di ciò che i programmatori più funzionali considerano comunque la migliore pratica (la separazione).
TL; DR;
Qualcosa come l'approccio sopra descritto è mai stato provato? Se no, c'è qualche ragione per cui (sia tecniche, pratiche, teoriche e filosofiche sono interessanti)?
E, infine, un tale controllo della purezza sarebbe più facile da accettare per le persone che affermano che Haskell non è pratico?
responsabilità
Personalmente non nutro rancore verso Haskell e la scelta di utilizzare le monadi per l'IO. In effetti, trovo che Haskell sia uno dei linguaggi più eleganti in uso e forse l'unica lingua che mi piace di più.
Inoltre, è chiaro che l'approccio sopra menzionato non offre qualcosa che la monade IO non fa - l'unica cosa è che esso è più strettamente (visivamente) simile a linguaggi impuri come OCaml, F # e così via, che è legato a rendilo più facile da capire per molte persone. In effetti, prendere il codice F # esistente e aggiungere alcune etichette "impure" sarebbe l'unica differenza visiva del codice.
Quindi, questo potrebbe essere un facile passo verso la purezza per le lingue che non hanno lo stesso ecosistema che circonda le monadi come Haskell.