一个Haskelltypes的函数:IO String-> String
我在Haskell中编写了一堆代码来创build一个文本索引。 顶层函数如下所示:
index :: String -> [(String, [Integer])] index a = [...]
现在我想给这个函数从文件中读取一个string:
index readFile "input.txt"
这将不起作用,因为readFile的types是FilePath – > IO String。
无法匹配预期types“string”对推断types“IOstring”
我看到错误,但我找不到任何types的函数:
IO String -> String
我想成功的关键在于某些Monad下的某个地方,但是我找不到解决问题的办法。
你可以很容易地编写一个调用readFile动作的函数,并将结果传递给你的索引函数。
readAndIndex fileName = do text <- readFile fileName return $ index text
但是,IO monad会污染使用它的所有东西,所以这个函数的types是:
readAndIndex :: FilePath -> IO [(String, [Integer])]
为什么没有这样的function有一个很好的理由。
Haskell具有function纯度的概念。 这意味着一个函数在调用相同的参数时总是返回相同的结果。 IO的唯一允许位置是IO monad。
如果有*function
index :: IO String -> String
那么我们可以通过调用突然间在任何地方执行IO操作,例如:
index (launchMissiles >> deleteRoot >> return "PWNd!")
function纯度是一个非常有用的function,我们不想丢失,因为它允许编译器更自由地对函数进行重新sorting和内联,可以在不改变语义的情况下将它们引发到不同的内核,同时也给程序员一个感觉因为如果你可以知道一个函数可以做什么,不能从它的types。
*其实有这样的function。 这就是所谓的unsafePerformIO
,这是非常非常好的原因。 除非你100%确定自己在做什么,否则不要使用它!
那么你不能摆脱IO String
的IO
monad部分。 这意味着你将不得不让你的函数返回IO [(String, [Integer])]
。
我build议学习更多单子,但是现在你可以用liftM
函数去掉:
liftM index (readFile "input.txt")
liftM
有这个签名:
liftM :: Monad m => (a -> b) -> ma -> mb
它采用非一元函数并将其转换为一元函数。
fmap index $ readFile "input.txt"
要么
readFile "input.txt" >>= return . index
你可能想看看monad和函子