STDOUT e la sua impurità


10

Ho letto molti libri e articoli sulla programmazione funzionale e mi vergogno ancora di non riuscire a capire con certezza alcuni concetti basilari.

Una delle idee principali della programmazione funzionale è che lo stesso input dovrebbe sempre produrre lo stesso output. Pertanto, per esempio, per definizione non è possibile eseguire query su database o scrivere file in un puro stile funzionale. Questo è ad esempio uno dei motivi per cui abbiamo bisogno delle monadi.

La domanda è: perché consideriamo l'uscita STDOUT come qualcosa di impuro? Sì, qualsiasi gestore di file è rischioso: non possiamo mai essere sicuri che i dati vengano sempre scritti. Ma che dire di STDOUT? Perché dovremmo pensarlo come qualcosa di inaffidabile? È più inaffidabile quella valutazione stessa? Voglio dire, possiamo sempre premere il grilletto e quindi interrompere il calcolo.

Risposte:


6

Pertanto, per esempio, per definizione non è possibile eseguire query su database o scrivere file in un puro stile funzionale. Questo è ad esempio uno dei motivi per cui abbiamo bisogno delle monadi.

Nessuno "ha bisogno" di monadi, questo è solo un modo per descrivere le cose. In effetti, probabilmente non è nemmeno il modo migliore. Una qualche forma di tipizzazione dell'effetto , tipi di unicità o un sistema basato sulla logica lineare completa sembrano più persuasivi in ​​teoria, ma sono tutti partenze più radicali da sistemi di tipo noti e più complicati da esprimere. L'IO monadico come si trova in Haskell è un compromesso tra usabilità e semplicità, dal momento che modella essenzialmente una programmazione assolutamente imperativa in un modo che coesiste facilmente con l'attuale sistema di tipi in stile ML già utilizzato nella lingua.

La domanda è: perché consideriamo l'uscita STDOUT come qualcosa di impuro? Sì, qualsiasi gestore di file è rischioso: non possiamo mai essere sicuri che i dati vengano sempre scritti. Ma che dire di STDOUT? Perché dovremmo pensarlo come qualcosa di inaffidabile? È più inaffidabile quella valutazione stessa? Voglio dire, possiamo sempre premere il grilletto e quindi interrompere il calcolo.

Non lo è, e noi no. L'input e l'output del programma nel suo insieme possono essere semplicemente considerati argomenti e risultati dal trattamento dell'intero programma come un'unica grande funzione pura. Fintanto che stampa la stessa cosa su stdout se la si nutre con la stessa cosa da stdin, è comunque una funzione pura. In effetti, prima di introdurre IO monadico, Haskell utilizzava un sistema I / O basato su stream che utilizzava flussi pigri puri per input e output. Lo lasciò cadere perché apparentemente era un dolore da usare, il che potrebbe darti un'idea del perché non hai sentito parlare di qualcosa del genere. :]

Per chiarire la questione in modo più sciocco, considera il linguaggio esoterico minimalista, Lazy K :

Lazy K è un linguaggio di programmazione funzionale, trasparente e trasparente dal punto di vista della raccolta, con un semplice sistema I / O basato su stream.

Ciò che distingue Lazy K da altre lingue simili è la sua quasi totale mancanza di altre funzionalità. Ad esempio, non offre un sistema di tipo polimorfico Hindley-Milner integrato. Non viene fornito con una vasta libreria standard con supporto per la programmazione GUI indipendente dalla piattaforma e collegamenti ad altre lingue. Né una libreria del genere può essere scritta poiché, tra le altre cose, Lazy K non fornisce alcun modo per definire o fare riferimento a funzioni diverse dai built-in. Questa incapacità è integrata da una mancanza corrispondente di supporto per numeri, stringhe o qualsiasi altro tipo di dati. Tuttavia, Lazy K è Turing-completo.

(...)

I programmi Lazy K vivono nello stesso regno platonico senza tempo delle funzioni matematiche, quello che la pagina Unlambda chiama "il regno benedetto del puro calcolo lambda non tipizzato". Proprio come la garbage collection nasconde il processo di gestione della memoria dal programmatore, così la trasparenza referenziale nasconde il processo di valutazione. Il fatto che siano necessari alcuni calcoli per visualizzare un'immagine dell'insieme di Mandelbrot o per "eseguire" un programma Lazy K è un dettaglio di implementazione. Questa è l'essenza della programmazione funzionale.

(...)

Come gestire input e output in una lingua senza effetti collaterali? In un certo senso, input e output non sono effetti collaterali; sono, per così dire, effetti frontali e posteriori. Così è in Lazy K, dove un programma viene semplicemente trattato come una funzione dallo spazio dei possibili input allo spazio dei possibili output.

Dubito che troverai un linguaggio più puramente funzionale di quello!


Tieni presente, tuttavia, che quanto sopra si applica essenzialmente solo a prendere l'input e l'output di una funzione pura e collegandoli allo stdin / stdout "esternamente" in qualche modo. C'è una grande differenza tra questo e l'accesso alle primitive I / O reali a livello di sistema. I dettagli di implementazione della lettura e della scrittura nei flussi possono perdere impurità a meno che non siano accuratamente incapsulati.

Mi aspetto che questo sia il motivo principale per cui non è possibile farlo direttamente in Haskell: i casi d'uso sensati sono snelli rispetto all'utilizzo dell'IO monadico e per quest'ultimo c'è un grande vantaggio nell'accesso alla cosa reale. Credo sia per questo che, ad esempio, gli argomenti della riga di comando al programma non vengono semplicemente passati come argomenti main, anche se sembra intuitivamente che dovrebbero esserlo.

Puoi recuperare una versione minima di qualcosa del genere in un programma specifico, tuttavia: basta catturare gli argomenti come valori puri e quindi utilizzare la interactfunzione per il resto del programma.


Signore, devo confessare, mi godo qualsiasi risposta su una delle pile. Dovresti assolutamente scrivere un libro Haskell e NON sto scherzando.
shabunc,

@shabunc: a volte mi chiedevo quanto la somma totale delle mie risposte su SO siano già le dimensioni di un libro ...
CA McCann,

Potresti dare un esempio di un sistema basato sulla logica lineare completa? Sembra interessante, se esiste.
configuratore

@configurator: noti come mi sono collegato a lingue specifiche per gli altri, ma una pagina di Wikipedia per la logica lineare? Purtroppo, se avessi un esempio, lo avrei dato. : [Tutto ciò di cui ho sentito parlare sono prototipi parziali e sistemi sperimentali della ricerca CS. Se vuoi approfondire questo, ecco del materiale relativamente accessibile su sistemi di tipo lineare che potrebbero iniziare.
CA McCann,

3

Mentre la purezza in un programma funzionale è un obiettivo degno, la realtà è che ogni programma non banale e utile avrà qualche impurità (o "effetti collaterali"), per i motivi che hai citato.

I programmi completamente puri sono, per definizione, una scatola nera sigillata e sono sostanzialmente poco interessanti.

Il linguaggio funzionale Haskell si occupa di questo problema isolando gli effetti collaterali come l'output in monadi . La monade conserva uno stile di programmazione puramente funzionale, pur consentendo di produrre output.


certo, hai ragione. Ma capisco perché la purezza al 100% è l'utopia. La mia domanda riguarda solo STDOUT.
shabunc,

1
STDOUT è un effetto collaterale, proprio come qualsiasi altro. Internamente, la monade eseguirà qualsiasi controllo degli errori che potrebbe essere richiesto.
Robert Harvey,

sì, è questa la domanda: perché è considerato un effetto collaterale come un altro?
shabunc,

2
Tutto ciò che modifica il mondo esterno è considerato un effetto collaterale.
Robert Harvey,

1

La scrittura su STDOUT può non riuscire se non si è connessi a un dispositivo terminale o se (per qualche motivo) si è chiuso il descrittore di file per esso.

Inoltre, STDOUT non è sempre "la schermata della console". A volte viene reindirizzato a un altro programma. A volte il tubo è rotto.


0

Aiuta se si pensa alla purezza in termini di "Cambia lo stato del mondo esterno". Ciò potrebbe includere la scrittura su una console, il file di registro, l'espulsione del CD o "Avvio dei missili".

Può anche essere un problema in termini di esecuzione simultanea. Se sai che una funzione non ha effetti collaterali, puoi facilmente organizzare la concorrenza in quanto puoi provare che non ci possono essere condizioni di gara o simili.


Cambia lo stato del mondo esterno o dipende dallo stato del mondo esterno. Vedi questa domanda per ulteriori discussioni in tal senso.
MatrixFrog,
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.