Функция Haskell типа: IO String-> String
Я написал кучу кода в Haskell, чтобы создать индекс текста. Верхняя функция выглядит так:
index :: String -> [(String, [Integer])]
index a = [...]
Теперь я хочу дать этой функции строку, считанную из файла:
index readFile "input.txt"
Что не будет работать, потому что readFile имеет тип FilePath → IO String.
Не удалось совместить ожидаемый тип 'String' против предполагаемого типа 'IO String'
Я вижу ошибку, но я не могу найти какую-либо функцию с типом:
IO String -> String
Я думаю, что ключ к успеху лежит где-то под некоторыми Монадами, но я не мог найти способ решить мою проблему.
Ответы
Ответ 1
Вы можете достаточно легко написать функцию, которая вызывает действие readFile, и передает результат вашей индексной функции.
readAndIndex fileName = do
text <- readFile fileName
return $ index text
Тем не менее, монашка IO уничтожает все, что ее использует, поэтому эта функция имеет тип:
readAndIndex :: FilePath -> IO [(String, [Integer])]
Ответ 2
Есть очень веская причина, почему такая функция отсутствует.
Haskell имеет понятие функциональной чистоты. Это означает, что функция всегда возвращает тот же результат при вызове с теми же параметрами. Место только, где разрешено IO, находится внутри монады IO.
Если была * функция
index :: IO String -> String
то мы могли бы внезапно совершать действия IO в любом месте, вызывая, например:
index (launchMissiles >> deleteRoot >> return "PWNd!")
Функциональная чистота - очень полезная функция, которую мы не хотим потерять, так как она позволяет компилятору изменять порядок и встроенные функции гораздо более свободно, их можно инициировать на разные ядра без изменения семантики, а также дает у программистов чувство безопасности, поскольку, если вы можете знать, что функция может и не может сделать из этого типа.
* На самом деле существует такая функция. Он назывался unsafePerformIO
, и он назвал это по очень, очень веским причинам. Не используйте его, если вы на 100% не уверены в том, что делаете!
Ответ 3
Ну, вы не можете избавиться от монадной части IO
IO String
. Это означает, что вам нужно будет вернуть функцию IO [(String, [Integer])]
.
Я рекомендую узнать больше о монадах, но теперь вы можете уйти с помощью функции liftM
:
liftM index (readFile "input.txt")
liftM
имеет такую подпись:
liftM :: Monad m => (a -> b) -> m a -> m b
Он принимает немонодическую функцию и преобразует ее в монадическую функцию.
Ответ 4
fmap index $ readFile "input.txt"
или
readFile "input.txt" >>= return . index
Возможно, вы захотите посмотреть на монады и функторы