Haskell奇怪的回归

时间:2011-08-15 04:12:47

标签: haskell haskell-platform

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)

3 个答案:

答案 0 :(得分:6)

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"

答案 1 :(得分:2)

添加MatrixFrog在答案中提到的内容。如果你看一下你的函数签名,即[String] -> Int -> [String],它表明这个函数是一个纯函数,并且不涉及任何副作用,就像在你的函数体中一样,你使用的doesFileExist具有签名String -> IO Bool其中IO的存在表明它是一个不纯的函数,即它涉及一些IO。在haskell中,不纯函数和纯函数之间存在严格的分离,事实上,如果你的函数调用了一些不实用的函数,那么你的函数也是不纯的。因此,在您的情况下,您的函数checkString需要是不纯的,并且可以通过使其返回IO [String]来完成,这是MatrixFrog在其答案中提到的。

另一方面,我建议你可以使功能如下:
checkString :: String -> IO (Maybe String),因为你的函数不需要整个字符串列表,因为它只需要列表中的特定字符串来完成它的工作而不是抛出错误你可以使用Maybe来检测错误。 这只是一个建议,但也取决于您的功能如何使用。

答案 2 :(得分:0)

我认为问题在于你的类型签名迫使do块假设它是其他一些monad。例如,假设您正在列表monad中工作。然后,你可以写

myFcn :: [String] -> Int -> [String]
myFcn p n = do
    return (p !! n)

对于列表monad,return只返回单例列表,因此您会得到类似的行为,

> myFcn ["a", "bc", "d"] 1
["bc"]

(我个人认为,如果GHC可以选择打印可能导致类型错误的常见错误,那将非常有用;我同情提问者,因为我收到了很多类型错误消息花点时间弄明白。)