How to filter [IO a]

Viewed 120

I have a function that reads the content of a file and tries to parse it to Int.

readIntFromFile :: String -> IO (Maybe Int)
readIntFromFile path = do
    s <- readFile path
    return $ readMaybe s

Let's say I called this function several times and ended up with [IO (Maybe Int)]. Having this list, I now want to filter Nothing values out, so the list contains only successfully parsed Int values. To make this happen, I need a function that looks like fltr :: [IO (Maybe Int)] -> [IO Int]. And this is where I stuck.

I can use sequence :: (Traversable t, Monad m) => t (m a) -> m (t a) to make IO [Maybe Int] out of [IO (Maybe Int)] and then filter function will be:

fltr2 :: IO [Maybe Int] -> IO [Int]
fltr2 k = k >>= onlyJust
    where onlyJust xl = return [y | Just y <- xl]  

Unfortunately, I don't think fltr2 can always be used instead of original fltr. What if one of the files I read with readIntFromFile does not exist?

I'm new to Haskell, and I suspect that I'm doing something fundamentally wrong here. Is there any way to implement filtering through [IO a] (in my case [IO (Maybe Int)])? And if it is possible to generalize what should be an approach to filter lists like (Monad m) => [m a]?

1 Answers

My first note would be that [IO (Maybe Int)] is not a very useful type most of the time, and typically you should prefer to wind up with IO [Maybe Int]. Supposing that you have a [String] somewhere that you're mapping this function over, you can use traverse (or its older synonym, mapM) to get the more useful type:

map readIntFromFile fileNames :: [IO (Maybe Int)]
traverse readIntFromFile fileNames :: IO [Maybe Int]

Having done that, you can use fmap to reach through the IO and perform operations on its contents. In this case, you want a function of type [Maybe Int] -> [Int]. If we look on Hoogle for [Maybe a] -> [a], we immediately find catMaybes. So, fmap catMaybes has the type you want, and thus

fmap catMaybes . traverse readIntFromFile $ fileNames :: IO [Int]
Related