2010-01-20 17 views
56

In un recente answer to a style question, ho scrittoCome si usa Control.Applicative per scrivere Cleaner Haskell?

main = untilM (isCorrect 42) (read `liftM` getLine) 

e

isCorrect num guess = 
    case compare num guess of 
    EQ -> putStrLn "You Win!" >> return True 
    ... 

Martijn alternative utilmente suggerite:

main = untilM (isCorrect 42) (read <$> getLine) 

EQ -> True <$ putStrLn "You Win!" 

Quali modelli comuni nel codice Haskell può essere resa più chiara con astrazioni da Control.Applicative ? Quali sono le utili regole pratiche da tenere a mente per l'utilizzo efficace di Control.Applicative?

risposta

42

Fondamentalmente, le monadi sono anche funtori applicativi [1]. Pertanto, ogni volta che ti trovi a utilizzare liftM, liftM2, ecc., Puoi concatenare il calcolo utilizzando <*>. In un certo senso, si può pensare ai funtori applicativi come analoghi alle funzioni. Una pura funzione f può essere revocata facendo f <$> x <*> y <*> z.

Rispetto alle monadi, i funtori applicativi non possono eseguire i suoi argomenti in modo selettivo. Gli effetti collaterali di tutti gli argomenti avranno luogo.

import Control.Applicative 

ifte condition trueClause falseClause = do 
    c <- condition 
    if c then trueClause else falseClause 

x = ifte (return True) (putStrLn "True") (putStrLn "False") 

ifte' condition trueClause falseClause = 
    if condition then trueClause else falseClause 

y = ifte' <$> (pure True) <*> (putStrLn "True") <*> (putStrLn "False") 

x uscite solo True, considerando y uscite True e False sequenzialmente.

[1] The Typeclassopedia. Altamente raccomandato.

[2] http://www.soi.city.ac.uk/~ross/papers/Applicative.html. Anche se questo è un documento accademico, non è difficile da seguire.

[3] http://learnyouahaskell.com/functors-applicative-functors-and-monoids#applicative-functors. Spiega molto bene l'accordo.

[4] http://book.realworldhaskell.org/read/using-parsec.html#id652399. Mostra come la libreria monodica Parsec può essere utilizzata anche in modo applicativo.

+3

Questo è l'unico posto che ho visto dove viene spiegata la differenza tra 'Monad' e' Applicative' (in termini di cosa è possibile esprimere con ciascuno). Ottimo lavoro! Possiamo chiamare "ifte" con le funzioni applicative: "z = ifte <$> ..."? –

+1

"ifte" è già una funzione monadica, quindi non c'è molto altro da sollevare ancora una volta. –

+3

È possibile scrivere "ifte" <$> (pure True) "come" ifte "True" - "puro" è raramente necessario in combinazione con "<$>" e "<*>" (se del tutto). – Martijn

45

C'è molto da dire in risposta alla tua domanda, tuttavia, dal momento che hai chiesto, offrirò questa "regola generale".

Se si utilizza do -notazione e i valori generati [1] non vengono utilizzati nelle espressioni che si stanno sequenziando [2], tale codice può essere trasformato in uno stile Applicativo. Allo stesso modo, se si utilizza uno o più valori generati in un'espressione sequenziata, è necessario utilizzare Monad e Applicative non abbastanza forte da ottenere lo stesso codice.

Per esempio, esaminiamo il seguente codice:

do a <- e1 
    b <- e2 
    c <- e3 
    return (f a b c) 

vediamo che in nessuna delle espressioni a destra del <- fare qualsiasi dei valori generati (a, b, c) appaiono. Pertanto, possiamo trasformarlo in usando il codice Applicativo.Qui è una possibile trasformazione:

f <$> e1 <*> e2 <*> e3 

e altro:

liftA3 f e1 e2 e3 

D'altra parte, prendere questo pezzo di codice per esempio:

do a <- e1 
    b <- e2 a 
    c <- e3 
    return (f b c) 

Questo codice non può utilizzare Applicative [3 ] perché il valore generato a viene utilizzato successivamente in un'espressione nella comprensione. Questo deve utilizzare Monad per ottenere il suo risultato - tentare di inserirlo in Applicative per capire perché.

Ci sono alcuni dettagli ulteriori interessanti e utili su questo argomento, però, ho solo lo scopo di darvi questa regola generale per cui è possibile scremare sopra un -comprehension do e determinare abbastanza rapidamente se può essere preso in considerazione in Applicative codice di stile .

[1] Quelle che appaiono a sinistra di <-.

[2] Espressioni visualizzate a destra di <-.

[3] in senso stretto, alcune parti di esso potrebbero, scendendo e2 a.

+12

A volte puoi mescolare gli operatori Monad e Applicative se ti piace lo stile applicativo. Il tuo secondo blocco può essere scritto 'f <$> (e1 >> = e2) <*> e3' – AndrewC

+3

+1 per esempi concreti di quando Applicativo è sufficiente, e quando Monad è richiesto. – sjy