Получение первого элемента списка в монаде IO - PullRequest
1 голос
/ 29 февраля 2020

Я пытаюсь получить первый элемент из цепочки монад ввода / вывода (насколько я понимаю, я все еще учусь Haskell):

getSomePath :: FilePath -> IO [FilePath]
getSomePath path = do
    pomFile <- listDirectory path >>= filterM (\x -> return $ x == "pom.xml") >>= head
    -- here I'll add some more files 
    return [pomFile, someOtherFile]

Ошибка появляется, когда я ' Я пытаюсь применить head функцию:

Couldn't match type ‘[Char]’ with ‘IO FilePath’
      Expected type: [[Char]] -> IO FilePath
        Actual type: [IO FilePath] -> IO FilePath
    • In the second argument of ‘(>>=)’, namely ‘head’
      In a stmt of a 'do' block:
        pomFile <- listDirectory path
                     >>= filterM (\ x -> return $ x == "pom.xml")
                     >>= head

Я также пытался:

pomFile <- head (listDirectory path >>= filterM (\x -> return $ x == "pom.xml"))

без результата. Как применить функцию head к типу IO [a], чтобы она получилась с IO a? Есть ли head эквивалент для применения его к монадам? Я знаю, что могу просто использовать System.Directory.findFile или что-то подобное, но я хотел понять, почему мой подход не работает.

1 Ответ

3 голосов
/ 29 февраля 2020

Вы не можете использовать head, поскольку аргумент здесь IO [a], а не a. Однако вы можете fmap :: Function f => (a -> b) -> f a -> f b на этом, и, таким образом, получить IO a, или использовать псевдоним оператора <$>.

При этом, используя filterM здесь не необходимо, вы можете просто отфильтровать список, а затем взять голову:

    pomFile <- (head . filter ("pom.xml" ==)) <b><$></b> listDirectory path

Однако вышеприведенное не безопасно . Если каталог содержит "pom.xml", он просто вернет FilePath (String), то есть "pom.xml". Если это не так, это вызовет ошибку, которая говорит о том, что вы хотите получить заголовок пустого списка.

Таким образом, вы, вероятно, можете просто работать с:

    hasPomFile <- elem "pom.xml" <$> listDirectory path

здесь hasPomFile таким образом, это Bool, то есть True, если каталог содержит "pom.xml", и False в противном случае.

...