Una funzione di tipo Haskell: IO String> Stringa
Domanda
ho scritto un mucchio di codice in Haskell per creare un indice di un testo. La funzione superiore è simile al seguente:
index :: String -> [(String, [Integer])]
index a = [...]
Ora voglio dare questa funzione una stringa di leggere da un file:
index readFile "input.txt"
Il che non funziona perché readFile è di tipo FilePath -.> IO Stringa
tipo non poteva competere previsto 'String' contro il tipo derivato 'IO String'
vedo l'errore, ma non riesco a trovare alcuna funzione con il tipo:
IO String -> String
Credo che la chiave del successo risiede da qualche parte sotto alcune Monadi, ma non riuscivo a trovare un modo per risolvere il mio problema.
Soluzione
Si può facilmente sufficiente scrivere una funzione che chiama l'azione readFile, e passa il risultato alla funzione indice.
readAndIndex fileName = do
text <- readFile fileName
return $ index text
Tuttavia, la monade IO contamina tutto ciò che lo utilizza, per cui questa funzione ha il tipo:
readAndIndex :: FilePath -> IO [(String, [Integer])]
Altri suggerimenti
C'è una buona ragione per cui non v'è tale funzione.
Haskell ha il concetto di purezza funzionale. Ciò significa che una funzione restituirà sempre lo stesso risultato quando viene chiamato con gli stessi parametri. Il solo luogo in cui è consentito IO è dentro la monade IO.
Se c'era una funzione *
index :: IO String -> String
allora potremmo improvvisamente fare azioni IO ovunque chiamando, ad esempio:
index (launchMissiles >> deleteRoot >> return "PWNd!")
purezza funzionale è una funzione molto utile che non vogliamo perdere, in quanto permette al compilatore di riordinare e funzioni inline molto più liberamente, si può essere innescato da diversi nuclei senza cambiare la semantica e dà anche la programmatori un senso di sicurezza dal momento che, se si può sapere che cosa una funzione può e non può fare dal suo tipo.
* In realtà ci è una tale funzione. Si chiama unsafePerformIO
e si chiama che per molto, molto buone ragioni. Non utilizzare a meno che non si è sicuri al 100% di quello che state facendo!
Beh, non è possibile liberarsi della parte IO
monade IO String
. Ciò significa che si dovrà fare il vostro IO [(String, [Integer])]
funzione di ritorno.
Mi consiglia di imparare di più su monadi, ma per ora si può farla franca con la funzione liftM
:
liftM index (readFile "input.txt")
liftM
ha questa firma:
liftM :: Monad m => (a -> b) -> m a -> m b
Si impiegano una funzione non monadico e la trasforma in una funzione monadica.
fmap index $ readFile "input.txt"
o
readFile "input.txt" >>= return . index
Si consiglia di guardare in monade e funtori