2013-02-22 10 views
11

Nel leggere le monadi, continuo a vedere frasi come "calcoli nella monade Xyz". Cosa significa per un calcolo essere "in" una certa monade?Haskell: calcolo "in una monade" - che significa?

Penso di avere una buona conoscenza di ciò che riguarda le monadi: consentire ai calcoli di produrre output che sono di solito di qualche tipo previsto, ma in alternativa o in aggiunta possono trasmettere alcune altre informazioni, come stato di errore, informazioni di registrazione, stato e così via, e consentire che questi calcoli siano concatenati.

Ma non capisco come si direbbe un calcolo "in" una monade. Questo si riferisce solo a una funzione che produce un risultato monadico?

Esempi: (Search "calcolo") in

+0

Oggigiorno preferisco riferirmi a questi come * azioni * o * calcoli *. Se sono prodotti da una funzione, potresti parlare di azioni parametriche o di funzioni monadiche, ma quest'ultimo termine è ambiguo. Potrebbe riferirsi a 'a -> m b', ma anche a' m (a -> b) '. Poiché quest'ultimo è usato in stile applicativo, preferisco un termine meno ambiguo. – ertes

+0

Assicurati di non perdere la risposta di C. A. McCann sotto. – ertes

risposta

11

Generalmente, un "computation in una monade" si intende non solo una funzione che restituisce un risultato monadica, ma tale funzione utilizzata all'interno di un blocco do, o come parte del secondo argomento di (>>=), o altro equivalente a quelle. La distinzione è rilevante per qualcosa che hai detto in un commento:

"Calcolo" si verifica in func f, dopo val estratto da input monad, e prima che il risultato sia incapsulato come monade. Non vedo come il calcolo di per sé sia ​​"dentro" la monade; sembra vistosamente "fuori" dalla monade.

Questa non è una cattiva modo di pensarci - infatti, do notazione incoraggia perché è un modo comodo per guardare le cose - ma si traduca in un intuito un po 'fuorviante. In nessun luogo viene "estratto" da una monade. Per capire perché, dimenticare (>>=) - è un'operazione composita che esiste per supportare la notazione do. La definizione più fondamentale di una monade sono tre funzioni ortogonali:

fmap :: (a -> b) -> (m a -> m b) 
return :: a -> m a 
join :: m (m a) -> m a 

... dove m è una monade.

Ora pensare a come implementare (>>=) con questi: a partire con argomenti di tipo m a e a -> m b, l'unica opzione sta usando fmap di ottenere qualcosa di tipo m (m b), dopo di che è possibile utilizzare join per appiattire "strati" annidati a prendi solo m b.

In altre parole, non vengono intraprese "out" della monade - invece, pensare al calcolo come andare profondo nella Monade, con fasi successive di essere compressi in un singolo strato di monade.

Si noti che le leggi di monade sono anche molto più semplici da questa prospettiva - in sostanza, si dice che quando si applica join non importa fino a quando l'ordine di nidificazione viene preservato (una forma di associatività) e che il livello monadico introdotto da return non fa nulla (un valore di identità per join).

+0

Grazie per questa discussione, questo ha prodotto alcuni progressi :-). In questa fase, leggendo la tua discussione, penso di non essere chiaro su cosa esattamente "la monade" si riferisce (qualcosa che ha strati e profondità?), In uno scenario che coinvolge possibilmente funzioni multiple che ogni ritorno valori monadici, composti con l'aiuto di fmap e join, e i loro stessi risultati monadici. – gwideman

+1

@gwideman: Nel senso più stretto, "una monade" in Haskell è la struttura matematica descritta da un costruttore di tipi e implementazioni appropriate di 'fmap',' return' e 'join', e" the monad "sarebbe lo specifico tipo costruttore e istanza in discussione. In pratica, il termine viene spesso anche leggermente abusato per fare riferimento ai costruttori di dati associati al costruttore del tipo e/o ai valori creati con essi. –

+0

La mia conclusione: "Il calcolo in 'Xyz Monad" significa "computazione in funzioni che restituiscono valori di tipo Xyz, con dette funzioni composte insieme usando i metodi fmap/return/join dell'istanza Mony di Xyz". – gwideman

0

consideri il IO monade. Un valore di tipo IO a è una descrizione di un numero elevato (spesso infinito) di comportamenti in cui un comportamento è una sequenza di eventi IO (letture, scritture, ecc.). Tale valore è chiamato "computazione"; in questo caso è un calcolo nella monade IO.

+0

Questo è davvero un esempio del tipo di commento in cui non capisco come si applica la preposizione "in". – gwideman

+0

Bene, abbiamo bisogno di combinare due frasi nominali "computazione" e "la monade IO". L'inglese ha solo un numero limitato di preposizioni. "Appartenere a" potrebbe avere più senso di "in". –

11

Fa semplicemente riferimento a una funzione che produce un risultato monadico?

Sì, in breve.


A lungo, è perché Monad permette di iniettare valori in esso (via return), ma una volta all'interno della Monad che sei bloccato. È necessario utilizzare alcune funzioni come evalWriter o runCont che è strettamente più specifica di Monad per ottenere i valori "indietro".

Più di questo, Monad (in realtà, il suo partner, Applicative) è l'essenza di avere un "contenitore" e consentire i calcoli di accadere al suo interno. Questo è ciò che ti offre (>>=), la possibilità di fare calcoli interessanti "all'interno" dello Monad.

Quindi funzioni come Monad m => m a -> (a -> m b) -> m b consentono di calcolare con e intorno e all'interno di un Monad. Funzioni come Monad m => a -> m a consentono di iniettare nel Monad. Funzioni come m a -> a consentono di "scappare" lo Monad tranne che non esistono in generale (solo in specifiche). Quindi, per ragioni di conversazione, ci piace parlare di funzioni con tipi di risultato come Monad m => m a come "dentro la monade".

+0

Grazie. "Per ragioni di conversazione" suggerisce "nella monade" è solo una vaga figura retorica, ma non sono abbastanza soddisfatto. "Così come funcs >> = [...] ritorno ..." Il mio problema: >> = e restituire funzioni a catena che assumono valori non monadici e restituiscono valori monadici: (uscite func valore monadico) -> (estrai la val chiusa usando >> =) -> (process val in func f) -> (wrap outval in monad). "Calcolo" si verifica in func f, dopo val estratto da input monad, e prima che il risultato sia incapsulato come monade. Non vedo come il calcolo di per sé sia ​​"dentro" la monade; sembra vistosamente "fuori" dalla monade. – gwideman

+0

@gwideman qualcosa su cui riflettere: uno dei tuoi esempi a cui ti sei collegato usa una monade di stato, che è un esempio in cui il bind è davvero composto: sta costruendo un computo stateful per essere infine "eseguito" su qualche stato iniziale. – jberryman

+0

Direi che sono "nella monade" perché (1) non restituiscono mai un valore nullo per te per ottenere una sospensione di "fuori" e (2) quando li leghi con '>> =' sei fondamentalmente iniettandoli nella 'Monade' e poi usando' join :: Monad m => m (ma) -> ma' per unire nuovamente gli strati. –

3

Solitamente le cose di monad sono più facili da comprendere quando si inizia con le monadi "collezione-like" come esempio. Immagina di calcolare la distanza di due punti:

data Point = Point Double Double 

distance :: Point -> Point -> Double 
distance p1 p2 = undefined 

Ora potresti avere un determinato contesto. Per esempio. uno dei punti potrebbe essere "illegale" perché è fuori da alcuni limiti (ad esempio sullo schermo).Quindi avvolgere il vostro calcolo esistente nel Maybe Monade:

distance :: Maybe Point -> Maybe Point -> Maybe Double 
distance p1 p2 = undefined 

Hai esattamente lo stesso calcolo, ma con la caratteristica aggiuntiva che ci possa essere "nessun risultato" (codificato come Nothing).

Oppure si dispone di due gruppi di punti "possibili", e hanno bisogno delle loro reciproche distanze (per esempio utilizzare in seguito la connessione più breve). Poi la lista monade è la tua "contesto":

distance :: [Point] -> [Point] -> [Double] 
distance p1 p2 = undefined 

O i punti vengono inseriti da un utente, che rende il calcolo "deterministica" (nel senso che si contano su cose nel mondo esterno, che può cambiare), quindi la monade IO è tuo amico:

distance :: IO Point -> IO Point -> IO Double 
distance p1 p2 = undefined 

il calcolo rimane sempre lo stesso, ma succede che si terrà in un determinato "contesto", che aggiunge alcuni aspetti utili (fallimento, più valori, non determinismo) . Puoi persino combinare questi contesti (trasformatori monad).

Si può scrivere una definizione che unifica le definizioni di cui sopra, e lavora per qualsiasi monade:

distance :: Monad m => m Point -> m Point -> m Double 
distance p1 p2 = do 
    Point x1 y1 <- p1 
    Point x2 y2 <- p2 
    return $ sqrt ((x1-x2)^2 + (y1-y2)^2) 

che dimostra che il nostro calcolo è davvero indipendenti dalla monade reale, che porta a formulazioni come "x è calcolato in (-side) la y monade".

+0

Grazie per lo sforzo, ma non vedo dove si ponga questa domanda. I due casi in cui discuti di "dentro" non hanno senso per me, ma forse mi sono perso qualcosa. In particolare: "Così avvolgi il tuo calcolo esistente nella forse monade". (a) questo calcolo non calcola la distanza, semplicemente restituisce un valore indefinito, quindi non sono sicuro di ciò che è stato illustrato. (b) Non "deforma il calcolo" in una monade, semplicemente specifica che i valori di input e di output sono vincolati al tipo Forse, che può funzionare come Monade se sono incluse definizioni appropriate, ... – gwideman

+0

[segue] anche se in questo esempio la capacità non sembra essere utilizzata. "Ciò dimostra che il nostro calcolo è realmente indipendente dalla monade effettiva, che porta alle formulazioni come" x è calcolato in (-side) la y monade ".Questo sembra più un non-sequitur. la monade, in che modo questo implica che il calcolo si trovi all'interno della monade? – gwideman

+1

(a) Ero troppo pigro per digitare. Pensavo sapessi come implementarlo (e l'ultimo blocco di codice contiene un'implementazione che funzionerebbe per lo specifico anche le monade) (b) Stai lavorando con valori avvolti ** in ** una monade, ma come ho provato a mostrare questo non influenza molto il calcolo.Questo rende il monad esterno intercambiabile, il che rende a sua volta necessario menzionalo in qualche modo quando ne parli: se i tuoi calcoli fossero più "hardcoded" verso una certa monade come "Maybe", non ti preoccuperei di sottolineare questo fatto. – Landei

1

Guardando i collegamenti che hai fornito, sembra che un uso comune di "computazione in" riguardi un singolo valore monadico. Estratti:

introduzione tesa - qui si corre un calcolo nel monade SM, ma il calcolo è il valore monadico:

-- run a computation in the SM monad 
runSM     :: S -> SM a -> (a,S) 

Tutto su monadi - precedente calcolo si riferisce ad un valore monadico nella sequenza :

la funzione >> è un operatore convenienza che viene utilizzato per legare una computazione monadic che non richiede input dal calcolo precedente nella sequenza

Informazioni sulle monadi: qui il primo calcolo potrebbe fare riferimento ad es. getLine, un valore monadico:

(binding) dà un'idea intrinseca usando il risultato di un calcolo in un altro calcolo, senza richiedere una nozione di calcoli esecuzione.

Così come analogia, se dico i = 4 + 2, quindi i è il valore 6, ma è ugualmente un calcolo, ovvero il calcolo 4 + 2. Sembra che le pagine collegate utilizzino il calcolo in questo senso - il calcolo come valore monadico - almeno in alcuni casi, nel qual caso ha senso utilizzare l'espressione "una computazione" nella data monade.

+0

Grazie per i commenti Boris. La domanda riguardava "computazione nella [qualcosa] monade", non solo "computazione in". Esistono diverse istanze del primo in ciascuno dei collegamenti referenziati. – gwideman