Как фильтровать [IO a]

#haskell

#хаскелл

Вопрос:

У меня есть функция, которая считывает содержимое файла и пытается проанализировать его Int .

 readIntFromFile :: String -gt; IO (Maybe Int) readIntFromFile path = do  s lt;- readFile path  return $ readMaybe s  

Допустим, я несколько раз вызывал эту функцию и в итоге получил [IO (Maybe Int)] . Имея этот список, я теперь хочу отфильтровать Nothing значения, чтобы список содержал только успешно проанализированные Int значения. Чтобы это произошло, мне нужна функция, которая выглядит так fltr :: [IO (Maybe Int)] -gt; [IO Int] . И вот здесь я застрял.

Я могу использовать sequence :: (Traversable t, Monad m) =gt; t (m a) -gt; m (t a) , чтобы сделать IO [Maybe Int] из [IO (Maybe Int)] , а затем функция фильтрации будет:

 fltr2 :: IO [Maybe Int] -gt; IO [Int] fltr2 k = k gt;gt;= onlyJust  where onlyJust xl = return [y | Just y lt;- xl]   

К сожалению, я не думаю fltr2 , что всегда можно использовать вместо оригинала fltr . Что делать, если один из файлов, с которыми я читаю, readIntFromFile не существует?

Я новичок в Хаскелле и подозреваю, что делаю здесь что-то в корне неправильное. Есть ли какой-либо способ реализовать фильтрацию через [IO a] (в моем случае [IO (Maybe Int)] )? И если можно обобщить, каким должен быть подход к спискам фильтров (Monad m) =gt; [m a] ?

Ответ №1:

Мое первое замечание будет заключаться [IO (Maybe Int)] в том, что в большинстве случаев это не очень полезный тип, и, как правило, вы предпочитаете заканчивать с IO [Maybe Int] ним . Предположим, что у вас есть [String] где-то, где вы сопоставляете эту функцию, вы можете использовать traverse (или ее более старый синоним mapM ), чтобы получить более полезный тип:

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

Сделав это, вы можете использовать fmap его для доступа IO и выполнения операций с его содержимым. В этом случае вам нужна функция типа [Maybe Int] -gt; gt; [Int] . Если мы поищем на Хугле [a]» rel=»nofollow noreferrer»> [Maybe a] -gt; [a] , то сразу найдем catMaybes . Итак, fmap catMaybes имеет нужный вам тип, и, таким образом,

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

Комментарии:

1. Спасибо вам за ответ. Еще одна вещь, которую я четко не описал в своем первоначальном вопросе, — это когда обрабатывать ошибки ввода-вывода? Например, что делать, если один из файлов, с которыми мы читаем, readIntFromFile не существует, и мы не хотим останавливать всю операцию? Единственное, что приходит на ум, — это переключиться readIntFromFile на что-то подобное readIntFromFile2 :: String -gt; IO (Either SomeException (Maybe Int)) и обработать исключение внутри fmap . Есть ли в этом какой-то смысл? Или есть лучший подход?