Domanda

Qualcuno potrebbe dare alcune indicazioni sul perché i calcoli impure a Haskell sono modellati come monadi?

Voglio dire monade è solo un'interfaccia con 4 operazioni, in modo da quello che era il ragionamento di modellazione effetti collaterali in esso?

È stato utile?

Soluzione

Si supponga che una funzione ha effetti collaterali. Se prendiamo tutti gli effetti che produce, come i parametri di ingresso e di uscita, quindi la funzione è puro al mondo esterno.

Quindi, per una funzione impura

f' :: Int -> Int

aggiungiamo il RealWorld alla considerazione

f :: Int -> RealWorld -> (Int, RealWorld)
-- input some states of the whole world,
-- modify the whole world because of the side effects,
-- then return the new world.

poi f è di nuovo pura. Definiamo un parametrizzata tipo di dati IO a = RealWorld -> (a, RealWorld), quindi non abbiamo bisogno di digitare RealWorld tante volte

f :: Int -> IO Int

Per il programmatore, la gestione di un RealWorld direttamente è troppo pericoloso, in particolare, se un programmatore ottiene le mani su un valore di tipo RealWorld, potrebbero tentare di copia , il che è praticamente impossibile. (Pensate di cercare di copiare l'intero file system, per esempio. Dove si direbbe?) Di conseguenza, la nostra definizione di IO incapsula gli stati di tutto il mondo pure.

Queste funzioni impuri sono inutili se non possiamo concatenare insieme. Considerare

getLine :: IO String               = RealWorld -> (String, RealWorld)
getContents :: String -> IO String = String -> RealWorld -> (String, RealWorld)
putStrLn :: String -> IO ()        = String -> RealWorld -> ((), RealWorld)

Vogliamo ottenere un nome di file dalla console, leggere il file, quindi stampare il contenuto fuori. Come possiamo farlo se siamo in grado di accedere gli stati del mondo reale?

printFile :: RealWorld -> ((), RealWorld)
printFile world0 = let (filename, world1) = getLine world0
                       (contents, world2) = (getContents filename) world1 
                   in  (putStrLn contents) world2 -- results in ((), world3)

Si vede un modello qui: le funzioni sono chiamate in questo modo:

...
(<result-of-f>, worldY) = f worldX
(<result-of-g>, worldZ) = g <result-of-f> worldY
...

Così potremmo definire un ~~~ all'operatore di legarli:

(~~~) :: (IO b) -> (b -> IO c) -> IO c

(~~~) ::      (RealWorld -> (b, RealWorld))
      -> (b -> RealWorld -> (c, RealWorld))
      ->       RealWorld -> (c, RealWorld)
(f ~~~ g) worldX = let (resF, worldY) = f worldX in
                        g resF worldY

allora potremmo semplicemente scrivere

printFile = getLine ~~~ getContents ~~~ putStrLn

senza toccare il mondo reale.


Ora supponiamo che vogliamo rendere il contenuto del file maiuscole pure. Il maiuscolo è una pura funzione

upperCase :: String -> String

Ma per farlo nel mondo reale, deve restituire un IO String. E 'facile sollevare una tale funzione:

impureUpperCase :: String -> RealWorld -> (String, RealWorld)
impureUpperCase str world = (upperCase str, world)

questo può essere generalizzato:

impurify :: a -> IO a

impurify :: a -> RealWorld -> (a, RealWorld)
impurify a world = (a, world)

in modo che impureUpperCase = impurify . upperCase, e possiamo scrivere

printUpperCaseFile = 
    getLine ~~~ getContents ~~~ (impurify . upperCase) ~~~ putStrLn

(Nota: Di norma si scrive getLine ~~~ getContents ~~~ (putStrLn . upperCase))


Ora vediamo quello che abbiamo fatto:

  1. definito un operatore che (~~~) :: IO b -> (b -> IO c) -> IO c catene due funzioni impure insieme
  2. definita una funzione impurify :: a -> IO a che converte un valore puro impura.

Ora facciamo il (>>=) = (~~~) identificazione e return = impurify, e vediamo? Abbiamo una monade.


(Per verificare se è davvero una monade c'è qualche assiomi devono essere soddisfatte:

(1) return a >>= f = f a

  impurify a               = (\world -> (a, world))
 (impurify a ~~~ f) worldX = let (resF, worldY) = (\world -> (a, world)) worldX 
                             in f resF worldY
                           = let (resF, worldY) =            (a, worldX))       
                             in f resF worldY
                           = f a worldX

(2) f >>= return = f

  (f ~~~ impurify) a worldX = let (resF, worldY) = impuify a worldX 
                              in f resF worldY
                            = let (resF, worldY) = (a, worldX)     
                              in f resF worldY
                            = f a worldX

(3) f >>= (\x -> g x >>= h) = (f >>= g) >>= h

Esercizio.)

Altri suggerimenti

  

Si poteva dare alcune indicazioni sul motivo per cui i calcoli unpure a Haskell sono modellati come monadi?

Questa domanda contiene un equivoco diffuso. L'impurità e Monade sono nozioni indipendenti. L'impurità è non modellati dalla Monade. Piuttosto, ci sono alcuni tipi di dati, come ad esempio IO, che rappresentano il calcolo imperativo. E per alcuni di questi, una piccola frazione del loro interfaccia corrisponde al modello di interfaccia chiamata "Monad". Inoltre, non si conoscono puro / funzionale spiegazione / denotativa di IO (e non è improbabile che sia uno, considerando il "sin bin" scopo di IO), anche se c'è la storia comunemente raccontato World -> (a, World) essere il significato di IO a. Quella storia non può sinceramente descrivere IO, perché IO supporta concorrenza e non determinismo. La storia non ha nemmeno funziona quando per i calcoli deterministiche che consentono l'interazione metà del calcolo con il mondo.

Per ulteriori spiegazioni, vedi questa risposta .

Modifica : On rileggere la domanda, non credo che la mia risposta è abbastanza in pista. Tutti i modelli di calcolo imperativo non spesso si rivelano essere monadi, proprio come ha detto la domanda. Il richiedente non potrebbe davvero pensare che monadness in qualche modo permette la modellazione di calcolo imperativo.

A quanto ho capito, qualcuno ha chiamato Eugenio Moggi notato prima che una precedenza oscura costrutto matematico chiamato "monade" potrebbe essere utilizzato per modellare gli effetti collaterali di linguaggi di programmazione, e quindi specificare la loro semantica utilizzando Lambda calcolo. Quando Haskell è stato in fase di sviluppo ci sono stati vari modi in cui i calcoli sono stati modellati impure (vedi Simon Peyton Jones' 'cilicio' carta per maggiori dettagli), ma quando Phil Wadler introdotto monadi è diventato rapidamente chiaro che questa è stata la risposta. E il resto è storia.

  

Si poteva dare alcune indicazioni sul motivo per cui i calcoli unpure a Haskell sono modellati come monadi?

Bene, perché Haskell è puro . Avete bisogno di un concetto matematico di distinguere tra calcoli unpure e puri su tipo di livello e di modellare programm flussi rispettivamente.

Questo significa che dovrete finire con un certo tipo IO a che i modelli di un calcolo unpure. Allora avete bisogno di conoscere modi di che unisce questi calcoli di cui applicare in sequenza (>>=) e sollevare un valore (return) sono i più evidenti e quelli di base.

Con questi due, hai già definito una monade (senza nemmeno pensarci di esso);)

Inoltre, monadi forniscono astrazioni molto generali e potenti , così tanti tipi di flusso di controllo possono essere convenientemente generalizzati nelle funzioni monadici come sequence, liftM o sintassi speciale, rendendo non unpureness un caso speciale.

monadi in programmazione funzionale e unicità tipizzazione (l'unica alternativa che conosco) per ulteriori informazioni.

Come dici tu, Monad è una struttura molto semplice. Una metà della risposta è: Monad è la struttura più semplice che potevamo dare all'altro-effettuare funzioni e poterli utilizzare. Con Monad possiamo fare due cose: siamo in grado di trattare un valore puro come un valore collaterale effettuare (return), e siamo in grado di applicare una funzione laterale effettuando un valore collaterale effettuare per ottenere un nuovo valore collaterale effettuare (>>=) . Perdere la capacità di fare una di queste cose sarebbe paralizzante, quindi il nostro tipo side-effettuazione deve essere "almeno" Monad, e si scopre Monad è sufficiente per implementare tutto quello che abbiamo bisogno di finora.

L'altra metà è: qual è la struttura più dettagliata potremmo dare ai "possibili effetti collaterali"? Possiamo certamente pensare lo spazio di tutti i possibili effetti collaterali come un set (l'unica operazione che richiede l'appartenenza). Siamo in grado di combinare due effetti collaterali facendo uno dopo l'altro, e questo darà luogo ad un effetto collaterale diverso (o forse lo stesso - se il primo era "calcolatore di arresto" e la seconda era "file di scrivere", allora il risultato di comporre questi è solo "spegnere il computer").

Ok, quindi cosa possiamo dire su questa operazione? E 'associative; vale a dire, se uniamo tre effetti collaterali, non importa quale ordine facciamo la combinazione. Se lo facciamo (file in scrittura quindi leggere presa) poi spegnere il computer, è lo stesso di fare file di scrittura, allora (leggi presa poi l'arresto computer). Ma non è commutativa: ( "file scrivere", quindi "il file delete") è un effetto collaterale diverso da ( "cancellare il file", quindi "scrittura file"). E noi abbiamo un'identità: "Group" l'effetto speciale lato "effetti collaterali" funziona ( "effetti collaterali", quindi "Delete file" è lo stesso effetto collaterale come solo "cancellare il file") A questo punto qualsiasi matematico sta pensando Ma i gruppi hanno inversi, e non c'è modo di invertire un effetto collaterale in generale; "Eliminazione dei file" è irreversibile. Così la struttura che abbiamo lasciato è quella di un monoide, il che significa che le nostre funzioni collaterali a effettuare dovrebbero essere monadi.

C'è una struttura più complessa? Sicuro! Potremmo dividere i possibili effetti collaterali in effetti filesystem basati su effetti di network-based e di più, e abbiamo potuto venire con regole più elaborate di composizione che conservano questi dettagli. Ma ancora una volta si tratta di: Monad è molto semplice, ma abbastanza potente per esprimere maggior parte delle proprietà ci stanno a cuore. (In particolare, associatività e gli altri assiomi ci permettono di testare la nostra applicazione in piccoli pezzi, con la sicurezza che gli effetti collaterali della applicazione combinata sarà lo stesso come la combinazione degli effetti collaterali dei pezzi).

In realtà è piuttosto un modo pulito per pensare di I / O in modo funzionale.

Nella maggior parte dei linguaggi di programmazione, è fare operazioni di input / output. In Haskell, immaginare scrivere codice non do le operazioni, ma per generare un elenco delle operazioni che si desidera fare.

Monadi sono solo abbastanza sintassi per esattamente questo.

Se volete sapere perché monadi in contrapposizione a qualcosa di diverso, credo che la risposta è che sono il miglior modo funzionale a rappresentare I / O che la gente potrebbe pensare a quando stavano facendo Haskell.

Per quanto ne so, il motivo è quello di essere in grado di includere gli effetti collaterali verifiche nel sistema tipo. Se vuoi sapere di più, ascoltare episodi coloro SE-Radio : Episodio 108: Simon Peyton Jones sulla programmazione funzionale e Haskell Episodio 72: Erik Meijer su LINQ

Sopra ci sono molto buone risposte dettagliate con background teorico. Ma io voglio dare il mio vista su Io monade. Io non sono esperto programmatore Haskell, quindi potrebbe essere è abbastanza ingenuo o addirittura sbagliate. Ma mi ho aiutato a che fare con IO monade in una certa misura (si noti, che non si riferisce ad altre monadi).

Prima di tutto voglio dire che, ad esempio con il "mondo reale" non è troppo chiaro per me come non possiamo accedere alle sue (del mondo reale) stati precedenti. Può essere che non fanno riferimento a calcoli monad affatto ma si desidera nel senso di trasparenza referenziale, che è generalmente presenta nel codice haskell.

Quindi vogliamo la nostra lingua (Haskell) per essere pura. Ma abbiamo bisogno di operazioni di input / output in quanto senza di loro il nostro programma non può essere utile. E queste operazioni non possono essere puri per loro natura. Quindi l'unico modo per affrontare questo dobbiamo separare le operazioni impuri dal resto del codice.

Ecco monade viene. A dire il vero, non sono sicuro, che non può esistere altro costrutto con simili proprietà necessarie, ma il punto è che monade hanno queste proprietà, quindi può essere usato (ed è utilizzato con successo). La struttura principale è che non si può sfuggire da essa. Interfaccia Monade non hanno operazioni per sbarazzarsi della monade intorno al nostro valore. Altri monadi (non IO) forniscano tali operazioni e permettono pattern matching (per esempio forse), ma tali operazioni non sono nell'interfaccia monade. Un'altra proprietà richiesto è la capacità di operazioni a catena.

Se pensiamo a quello che ci serve in termini di sistema di tipo, veniamo al fatto che abbiamo bisogno di scrivere con il costruttore, che può essere avvolto intorno a qualsiasi Vale. Costruttore deve essere privata, come noi proibiamo fuga da esso (cioè. Pattern matching). Ma abbiamo bisogno della funzione di mettere in valore questo costruttore (qui di ritorno viene in mente). E abbiamo bisogno del modo di operazioni a catena. Se ci pensiamo bene per un certo tempo, arriveremo al fatto, che il concatenamento operazione deve avere tipo >> = ha. Così, veniamo a qualcosa di molto simile alla monade. Credo che, se noi ora ad analizzare le possibili situazioni contraddittorie con questo costrutto, arriveremo a assiomi Monade.

Nota, che ha sviluppato costrutto non hanno niente in comune con l'impurità. Si hanno solo proprietà, che abbiamo voluto avere per essere in grado di affrontare con le operazioni impure, cioè non-escape, concatenamento, e un modo per entrare.

Ora un insieme di operazioni impure è predefinito dal linguaggio all'interno di questa monade selezionato IO. Siamo in grado di combinare le operazioni per creare nuove operazioni unpure. E tutte quelle operazioni dovranno avere IO nella loro tipo. Si noti tuttavia, che la presenza di IO nel tipo di alcune funzioni non fanno questa funzione impura. Ma, come ho capito, è cattiva idea di scrivere funzioni pure con IO nel loro tipo, come era inizialmente la nostra idea di separare le funzioni di puri e impuri.

Infine, voglio dire, che monade non girare operazioni impuri in quelli puri. Esso consente solo di separare in modo efficace. (Ripeto, che è solo la mia comprensione)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top