管理IO Monad

5

我正在学习一些Haskell(请原谅我的新手错误)-

这个程序失败了。 我对 do 和 <- 语法的理解是它们从单子中提取非单子类型。 所以这种理解是有缺陷的:这里的正确理解是什么?

exister :: String -> Bool
exister path = do
  fileexist <- doesFileExist path 
  direxist <- doesDirectoryExist path
  return fileexist || direxist 

错误

ghc -o joiner joiner.hs

joiner.hs:53:2:
    Couldn't match expected type `Bool' against inferred type `m Bool'
    In the first argument of `(||)', namely `return fileexist'
    In the expression: return fileexist || direxist
    In the expression:
        do { fileexist <- doesFileExist path;
             direxist <- doesDirectoryExist path;
               return fileexist || direxist }

8
你无法逃避IO monad。 - hammar
1
根据您下面的评论,您可能想查看这个问题及其答案:https://dev59.com/FFnUa4cB1Zd3GeqPWw2a(我们真的需要一个适当的常见问题解答)。 - acfoltzer
我认为指出Haskell的return与大多数语言中的“返回”并不相似可能会有所帮助。如果你想要一个以数字n为参数,然后“返回”n+1的函数,你应该写成f n = n + 1,而不是f n = return n+1 - Tyler
2个回答

11

第一个问题:代码中的行return fileexist || direxist被解析为(return fileexist) || direxist,你不能将m Bool作为||操作符的第一个参数传递。请将其改为return (fileexist || direxist)

第二个问题:你声称exister的返回类型是Bool,但编译器推断它必须是IO Bool。修复这个问题。(do<-语法让你从m a值中提取a值,但前提是你承诺要返回一个m a值。)


我向您保证,我希望exister返回一个Bool,而不是IO Bool。 - Paul Nathan
7
@Paul:这是不可能的。文件/目录是否存在取决于环境,这是一种不纯的函数,因此必须返回 IO Bool。纯函数必须在相同的输入下始终返回相同的输出。这显然在这里不是这种情况。 - hammar
@hammar:所以如果我的整个程序都是围绕着从硬盘/网络中提取数据,处理它并发送出去的话,我必须在所有计算中与IO单子打交道吗? - Paul Nathan
9
@Paul,你的计算可以是纯的,但是你的操作显然涉及IO。例如,一个存在于IO单子中的main程序可以从磁盘或网络获取数据(例如:x <- getData),将该数据传递给一个纯计算过程(例如:let result = computationOn x),然后发送结果(sendOnNetwork result)。 - Thomas M. DuBuisson
@Thomas:嗯,我感觉到学习曲线要来了。 :) 谢谢。 - Paul Nathan
1
@Paul Nathan,我在刚开始时也遇到了你完全相同的问题。幸运的是,学习曲线值得付出努力。 - Tim Perry

5
你提供的类型 exister :: String -> Bool 是一个返回普通非单子类型 Bool 的函数。你正在执行的操作 doesFileExist pathdoesDirectoryExist path 具有类型 IO Bool,因此错误消息中的 m Bool 实际上是指 IO Bool。如果将 exister 的类型更改为返回 IO Bool,则类型将符合您的意图。
另外,在最后一行,你需要加上一些括号:return (fileexist || direxist)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接