【问题标题】:Weird return in HaskellHaskell 的奇怪回归
【发布时间】:2011-08-15 04:12:47
【问题描述】:
checkstring :: [String] -> Int -> [String]
checkstring p n = do z <- doesFileExist (p !! n)
if z
then p
else error $ "'" ++ (p !! n) ++ "' file path does not exist"
它通过查看“n”来检查字符串中的元素(因此如果 n = 2,它将检查列表中的第二个字符串是否存在)然后查看它是否存在。如果确实存在,它将返回原始字符串列表,如果不存在,则会出错。为什么要这样做? :
Couldn't match expected type `[t0]' with actual type `IO Bool'
In the return type of a call of `doesFileExist'
In a stmt of a 'do' expression: z <- doesFileExist (p !! n)
【问题讨论】:
标签:
haskell
haskell-platform
【解决方案1】:
doesFileExist 的类型是String -> IO Bool。如果你的程序想知道一个文件是否存在,它必须与文件系统交互,这是一个 IO 动作。如果你想让你的 checkString 函数做到这一点,它还必须有某种基于 IO 的类型。例如,我认为这样的事情会起作用,虽然我还没有尝试过:
checkstring :: [String] -> Int -> IO [String]
checkstring p n = do z <- doesFileExist (p !! n)
if z
then return p
else error $ "'" ++ (p !! n) ++ "' file path does not exist"
【解决方案2】:
补充 MatrixFrog 在他的回答中提到的内容。如果您查看您的函数签名,即[String] -> Int -> [String],它表明该函数是一个纯函数并且不涉及任何副作用,而在您的函数体中,您使用的是doesFileExist,其签名为String -> IO Bool,其中IO 的存在表明它是一个不纯的函数,即它涉及一些 IO。在haskell中,不纯函数和纯函数之间有严格的区分,事实上,如果你的函数调用了其他一些不纯的函数,那么你的函数也是不纯的。因此,在您的情况下,您的函数 checkString 需要不纯,这可以通过使其返回 IO [String] 来完成,这就是 MatrixFrog 在他的回答中提到的。
另一方面,我建议您可以使函数类似于:
checkString :: String -> IO (Maybe String),因为您的函数不需要整个字符串列表,因为它只需要列表中的特定字符串做它的工作,而不是抛出错误,你可以使用 Maybe 来检测错误。
这只是一个建议,但它也取决于您的函数的使用方式。
【解决方案3】:
我认为问题在于您的类型签名强制 do 块假定它是其他一些单子。例如,假设您正在使用 list monad。然后,你可以写
myFcn :: [String] -> Int -> [String]
myFcn p n = do
return (p !! n)
在 list monad 的情况下,return 只是返回单例列表,所以你会得到类似的行为,
> myFcn ["a", "bc", "d"] 1
["bc"]
(我个人的看法是,如果 GHC 可以选择打印出可能导致类型错误的常见错误,那将非常有帮助;我很同情提问者,因为我收到了很多类型错误消息花点时间弄清楚)。