我将尝试从一个简化的示例开始。假设这是我们想要做的:
- 打开一个包含整数列表的文件并返回它。
- 对此列表进行排序
- 让我们也颠倒一下列表
- 在屏幕上打印结果
假设我们有这些函数可以使用:
getContent :: IO [Int]
sort :: [Int] -> [Int]
reverse :: [Int] -> [Int]
show :: a -> String
putStrLn :: String -> IO ()
为了让我们清楚,我会谈谈这些功能:
-
getContent:这个函数是我编的,但是如果有这样的函数 that 就是它的签名(你可以使用getContent = return [3,7,2,1] 进行测试)。我敢肯定你以前见过这样的签名,至少隐约明白,既然它做 IO,它的签名不能只是getContent :: [Int]。
-
sort:是Data.List模块中定义的函数,用法很简单:sort [3,1,2]返回[1,2,3]
-
reverse:也在Data.List模块中定义:reverse [1,3,2]返回[2,3,1]
-
show:不需要导入任何东西,直接使用即可:show 11返回字符串"11"; show [1,2,3] 返回字符串 "[1,2,3]" 等。
- putStrLn:获取一个字符串,将其放在屏幕上并返回 IO (),现在又一次,因为它执行 IO,所以它的签名不能只是
putStrLn :: Stiring -> ()。
好的,现在我们已经有了创建程序所需的一切,现在的问题是将这些功能连接在一起。让我们从连接函数开始:
getContent :: IO [Int] 与 sort :: [Int] -> [Int]
我认为如果你得到了这部分,你也很容易得到其余部分。所以,问题是因为getContent 返回IO [Int] 而不仅仅是[Int],你不能忽略或去掉IO 部分并将其推入sort。也就是说,这是你不能做的来连接这些功能:
sort (getRidOfIO getContent)
这就是>>= :: m a -> (a -> m b) -> m b 操作的用武之地。现在注意到m、a 和b 是类型变量,所以如果我们用m 代替IO,a 代替[Int] 和b 代替@987654354 @,我们得到签名:
>>= :: IO [Int] -> ([Int] -> IO [Int]) -> IO [Int]
再次查看getContent 和sort 函数及其签名,并尝试考虑如何将它们融入>>=。我相信你会注意到你可以直接使用getContent 作为>>= 的第一个参数。到目前为止,>>= 所做的是将[Int] 取出getContent 并将其推入作为第二个参数提供的函数中。但是第二个参数中的函数是什么?我们不能直接使用sort :: [Int] -> [Int],我们可以尝试的下一个最好的方法是
\listOfInts -> sort listOfInts
但这仍然有签名[Int] -> [Int],所以这并没有多大帮助。这里是另一个英雄来的地方,
return :: a -> m a.
同样,a 和 m 是类型变量,让我们替换它们,我们将得到
return :: [Int] -> IO [Int]
所以将\listOfInts -> sort listOfInts 和return 加在一起,我们将得到:
\listOfInts -> return $ sort listOfInts :: [Int] -> IO [Int]
这正是我们想要作为>>= 的第二个参数的内容。因此,让我们最终使用我们的胶水将getContent 和sort 连接在一起:
getContent >>= (\listOfInts -> return $ sort listOfInts)
这与(使用do 表示法)相同:
do listOfInts <- getContent
return $ sort listOfInts
到这里,最可怕的部分就结束了。现在可能是 aha 时刻之一,试着想想我们刚刚建立的连接的结果类型是什么。我会为你破坏它,......的类型
getContent >>= (\listOfInts -> return $ sort listOfInts) 又是IO [Int]。
让我们总结一下:我们采用了 IO [Int] 类型的东西和 [Int] -> [Int] 类型的东西,将这两个东西粘在一起,再次得到 IO [Int] 类型的东西!
现在继续尝试完全相同的事情:获取我们刚刚创建的IO [Int] 对象并将其与reverse :: [Int] -> [Int] 粘合在一起(使用>>= 和return)。
我觉得我写的太多了,但是如果有什么不清楚的地方或者您需要其他方面的帮助,请告诉我。
到目前为止,我所描述的内容可能如下所示:
getContent :: IO [Int]
getContent = return [5,2,1,7]
main :: IO ()
main = do
listOfInts <- getContent
return $ sort listOfInts
return () -- This is only to sattisfy the signature of main