2010-09-14 4 views
6

So che il seguente "fare" la funzione "legare" di notazione è equivalente a getLine >>= \line -> putStrLnConvertire una notazione "fare" con più di due azioni di utilizzare la funzione bind

do line <- getLine 
    putStrLn line 

Ma come è la seguente notazione equivalente legare la funzione?

do line1 <- getLine 
    putStrLn "enter second line" 
    line2 <- getLine 
    return (line1,line2) 
+4

Il primo esempio potrebbe essere molto più naturale scritto come 'GetLine >> = putStrLn'. La notazione è piuttosto scomoda lì. – Chuck

risposta

16

Suppongo che stiate provando a vedere come associare il risultato di "putStrLn". La risposta è nel tipo di putStrLn:

putStrLn :: String -> IO() 

Ricorda che "()" è il tipo di unità, che ha un valore singolo (scritto anche "()"). Quindi puoi associare questo esattamente allo stesso modo. Ma dal momento che non ne fanno uso si associa a un valore "do not care":

getLine >>= \line1 -> 
putStrLn "enter second line" >>= \_ -> 
getline >>= \line2 -> 
return (line1, line2) 

Si dà il caso, v'è un operatore già definito per ignorare il valore di ritorno, ">>". Quindi, si può solo riscrivere questo come

getLine >>= \line1 -> 
putStrLn "enter second line" >> 
getline >>= \line2 -> 
return (line1, line2) 

Non sono sicuro se si sta anche cercando di capire come gli operatori si legano in daisy-chain. Per vedere questo, mi permetta di mettere le staffe implicite e indentazione maggiore nell'esempio di cui sopra:

getLine >>= (\line1 -> 
    putStrLn "enter second line" >> (
     getline >>= (\line2 -> 
     return (line1, line2)))) 

Ogni operatore si legano collega il valore di una funzione per la destra a sinistra. Quella funzione consiste di tutte le altre linee nella clausola "do". Quindi la variabile che viene vincolata attraverso lambda ("line1" nella prima riga) è in scope per tutto il resto della clausola.

3
getLine >>= \line1 -> 
putStrLn "enter second line" >> 
getLine >>= \line2 -> 
return (line1, line2) 

Generalmente foo <- bar diventa bar >>= \foo -> e baz diventa baz >> (meno che non sia l'ultima riga del do-blocco, nel qual caso solo rimane baz).

7

Per questo esempio specifico si può effettivamente evitare sia do e >>= utilizzando combinatori da Control.Applicative:

module Main where 
import Control.Applicative ((<$>), (<*>), (<*)) 

getInput :: IO (String, String) 
getInput = (,) <$> getLine <* putStrLn "enter second line" <*> getLine 

main = print =<< getInput 

Che funziona come previsto:

[email protected]% ./Main 
hello 
enter second line 
world 
("hello","world") 

sembra un po 'strano in un primo momento, ma a mio parere lo stile applicativo sembra molto naturale una volta che ci si abitua.

3

Consiglio vivamente di leggere il capitolo Desugaring of Do-blocks nel libro "Real-World haskell". Ti dice che hai tutti torto. Per un programmatore, è il modo naturale di utilizzare una lambda, ma il blocco di esecuzione viene implementato utilizzando funzioni che, se si verifica un errore di pattern maching, chiameranno l'implementazione fail della monade corrispondente.

Per esempio, il caso è come:

let f x = 
     putStrLn "enter second line" >> 
     let g y = return (x,y) 
      g _ = fail "Pattern mismatched" 
     in getLine >>= g 
    f _ = fail "Pattern mismatched" 
in getLine >>= f 

In un caso come questo, questo può essere del tutto irrilevante. Ma considera alcune espressioni che coinvolgono la corrispondenza del modello.Inoltre, è possibile utilizzare questo effetto per alcune cose particolari, per esempio, si può fare qualcosa di simile:

oddFunction :: Integral a => [a] -> [a] 
oddFunctiond list = do 
    (True,y) <- zip (map odd list) list 
    return y 

Quale sarà questa funzione fare? Puoi leggere questa dichiarazione come regola per lavorare con gli elementi della lista. La prima istruzione lega un elemento della lista al var y, ma solo se y è dispari. Se y è pari, si verifica un errore di corrispondenza del modello e verrà chiamato fail. Nell'istanza monad per Elenchi, fail è semplicemente []. Pertanto, la funzione rimuove tutti gli elementi pari dalla lista.

(lo so, oddFunction = filter odd farebbe questo meglio, ma questo è solo un esempio)

Problemi correlati