Problema con Haskell del “fare”
Domanda
ho scritto una funzione Haskell che calcola il fattoriale di ogni numero in una data lista e lo stampa sullo schermo.
factPrint list =
if null list
then putStrLn ""
else do putStrLn ((show.fact.head) list)
factPrint (tail list)
La funzione funziona, ma trovo la terza linea un po 'di confusione. Perché il compilatore (GHC) non ha segnalato un errore su di essa dal momento che non c'è un "fare" prima che la funzione "putStrLn" (quasi?)? Se Tralascio "sì" dal 4 ° linea, un errore si apre come previsto.
Sono abbastanza nuovo per Haskell e dei suoi modi, quindi per favore mi perdoni se ho detto qualcosa di troppo stupido.
Soluzione
do putStrLn ((show.fact.head) list)
factPrint (tail list)
è in realtà un altro modo di scrivere
putStrLn ((show.fact.head) list) >> factPrint (tail list)
, che, a sua volta, significa
putStrLn ((show.fact.head) list) >>= \_ -> factPrint (tail list)
La notazione do
è un modo conveniente di infilare queste monadi insieme, senza quest'altra sintassi brutto.
Se si dispone di una sola istruzione all'interno della do
, allora non siete tesatura niente insieme, e il do
è ridondante.
Altri suggerimenti
Se siete nuovi a Haskell, pensare a do
come simile a parentesi graffe richiesti dopo if
in un linguaggio simile al C:
if (condition)
printf("a"); // braces not required
else {
printf("b"); // braces required
finish();
}
do
funziona allo stesso modo in Haskell.
Forse sarebbe d'aiuto a guardare il tipo di factPrint, e quindi refactoring di utilizzare pattern matching:
factPrint :: [Int] -> IO ()
factPrint [] = putStrLn ""
factPrint list = do
putStrLn (show.fact.head) list
factPrint (tail list)
Quindi, se factPrint torna IO ()
, e il tipo di putStrLn ""
è IO ()
, allora è perfettamente legale per factPrint []
alla parità putStrLn ""
. No do
richiesto -. In realtà, si può solo dire che factPrint [] = return ()
se non si desidera che il fine riga
do
viene utilizzato per legare più espressioni monadici insieme. Non ha alcun effetto se seguito da una sola espressione.
Per il caso di essere ben formato è necessario soltanto che l'allora clausola e l'altro clausola sono dello stesso tipo. Poiché entrambe le clausole di avere il tipo IO ()
questo è il caso.
La parola chiave do viene utilizzato per il sequenziamento, un if-then-else in Haskell non deve contenere una do
a tutti se ogni ramo è una singola istruzione ad es.
if a
then b
else c
È necessario il do
nel tuo esempio, come si sequenziazione due operazioni sul ramo altro. Se si omette il do
poi la dichiarazione factPrint(tail list)
è considerato non essere parte della funzione e quindi il compilatore si lamenta come è incontrato una dichiarazione inaspettata.