Сложно понять, что такое Haskell, как распознать и составить действия ввода-вывода.Давайте посмотрим на некоторые типы подписей.
dir :: FilePath
allFilesIn :: FilePath -> IO [FilePath]
perFileFunc :: Int -> FilePath -> IO (Map.Map [Char] Double)
Итак, вы сказали, что для myFunc
:
я ожидал список карт
Итак, для этой функции вам нужна сигнатура типа
myFunc :: Int -> FilePath -> [Map.Map String Double]
Конечно, тип возвращаемого значения не может быть просто [Map.Map String Double]
, потому что нам нужно выполнить некоторый IO вДля оценки myFunc
.Таким образом, учитывая Int
и FilePath
, мы фактически хотим, чтобы тип возвращаемого значения был IO action , который производит a [Map.Map String Double]
:
myFunc :: Int -> FilePath -> IO [Map.Map String Double]
Теперь давайте посмотрим на действия ввода-вывода, которые мы будем составлять для создания этой функции.
allFilesIn dir :: IO [FilePath]
perFileFunc n :: FilePath -> IO (Map.Map String Double)
perFileFunc
на самом деле не является действием ввода-вывода, но является функциейэто, учитывая FilePath
, производит действие ввода-вывода.Итак, давайте посмотрим ... если мы запустим действие allFilesIn
, тогда мы сможем работать с этим списком и запускать perFileFunc n
для каждого из его элементов.
myFunc dir n = do
files <- allFilesIn dir
???
Так что же происходит в ???
место?В нашем распоряжении [FilePath]
, так как мы использовали <-
для запуска действия allFilesIn dir
.И у нас есть функция, perFileFunc n :: FilePath -> IO (Map.Map String Double)
.И мы хотим, чтобы результат имел тип IO [Map.Map String Double]
.
Стоп ... Hoogle time!Обобщая имеющиеся у нас компоненты (a = FilePath
, b = Map.Map String Double
), мы hoogle для [a] -> (a -> IO b) -> IO [b]
(делая вид, что мы еще не видели ответа ehird).И вот, mapM
- это волшебное решение, которое мы искали!(или forM
, то есть просто flip mapM
)
myFunc dir n = do
files <- allFilesIn dir
mapM (perFileFunc n) files
Если вы откажетесь от этой записи do, вы обнаружите, что она сводится к ответу ehird:
myFunc dir n = allFilesIn dir >>= (\files -> mapM (perFileFunc n) files)
-- eta reduce (\x -> f x) ==> f
myFunc dir n = allFilesIn dir >>= mapM (perFileFunc n)