Una funzione Haskell di tipo: IO String-> String

Ho scritto un mucchio di codice in Haskell per creare un indice di un testo. La funzione top si presenta così:

index :: String -> [(String, [Integer])] index a = [...] 

Ora voglio dare a questa funzione una stringa letta da un file:

 index readFile "input.txt" 

Quale non funzionerà perché readFile è di tipo FilePath -> IO String.

Imansible concordare il tipo previsto “String” con il tipo inferito “IO String”

Vedo l’errore, ma non riesco a trovare alcuna funzione con tipo:

 IO String -> String 

Immagino che la chiave del successo risieda in qualche Monade, ma non sono riuscito a trovare un modo per risolvere il mio problema.

Puoi facilmente 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 tinge tutto ciò che la usa, quindi questa funzione ha il tipo:

 readAndIndex :: FilePath -> IO [(String, [Integer])] 

C’è una buona ragione per cui non esiste tale funzione.

Haskell ha la nozione di purezza funzionale. Ciò significa che una funzione restituirà sempre lo stesso risultato quando viene chiamata con gli stessi parametri. L’ unico posto dove è permesso IO è all’interno della monade IO.

Se ci fosse * una funzione

 index :: IO String -> String 

quindi potremmo improvvisamente eseguire azioni IO ovunque chiamando, ad esempio:

 index (launchMissiles >> deleteRoot >> return "PWNd!") 

La purezza funzionale è una caratteristica molto utile che non vogliamo perdere, poiché consente al compilatore di riordinare e inline funzioni molto più liberamente, possono essere avviate a diversi core senza cambiare la semantica e dà anche senso ai programmatori di sicurezza dal momento che se puoi sapere cosa una funzione può e non può fare dal suo tipo.

* In realtà esiste una tale funzione. Si chiama unsafePerformIO e si chiama così per ottime ragioni. Non usarlo se non sei sicuro al 100% di quello che stai facendo!

Bene, non puoi sbarazzarti della parte IO monade di IO String . Ciò significa che dovrai rendere la funzione return IO [(String, [Integer])] .

Raccomando di saperne di più sulle monadi, ma per ora puoi liftM con la funzione liftM :

 liftM index (readFile "input.txt") 

liftM ha questa firma:

 liftM :: Monad m => (a -> b) -> ma -> mb 

Prende una funzione non monadica e la trasforma in una funzione monadica.

 fmap index $ readFile "input.txt" 

o

 readFile "input.txt" >>= return . index 

Potresti voler esaminare monadi e funtori