当代码中没有可见列表时,为什么[String]是推断类型?

时间:2010-07-20 06:27:16

标签: haskell type-inference

我有这个测试:

testReadFile = runTestTT $ TestLabel "InteractionDomain.readFileContentsToList" (TestList [
    (TestLabel "testing infrastructure: read test file" (TestList [
        TestCase (withTempFileContainingText (\fileHandle ->
            assertEqual "contents as expected" "The dog barks at midnight." (do 
                content <- hGetContents fileHandle
                return content)))]))])

和(如果有帮助)withTempFileContainingText就是这个函数

withTempFileContainingText lambda = 
        bracketOnError 
            (do (path, fileHandle) <- openTempFile "." "input.txt"
                return fileHandle)
            (deleteFile)
            (\fileHandle -> do 
                hPutStr fileHandle "The dog barks at midnight."
                hClose fileHandle --hoping this flushes buffers
                return (lambda fileHandle))

ghc抱怨

InteractionDomain.hs:36:4:
    Couldn't match expected type `IO String'
           against inferred type `[String]'
    In a stmt of a 'do' expression: content ≤- hGetContents fileHandle

我不明白。为什么推断content是一个字符串列表?这段代码应该是什么样的?

3 个答案:

答案 0 :(得分:4)

嗯,我马上看到的是你断言中的类型不匹配。

assertEqual是String -> a -> a -> Assertion。您传递String作为第二个参数,这意味着String也应该是第三个参数。但是,您的do表达式不会返回String,而是返回IO String

编辑:要扩展,一旦你弄乱IO,你就不能放弃它。您使用<-正确地从IO中提取了值,但随后立即将其重新包装到IO return。如果要使用该字符串,则必须在IO内进行,例如:

do
  contents <- hGetContents handle
  assertEqual "They're equal" "Expected string" contents

请注意,您do将返回IO Assertion。如果要使用断言值,则需要以类似方式展开它,依此类推。 Haskell不会让你逃避(隐藏)副作用!

答案 1 :(得分:3)

assertEqual的类型是

assertEqual :: String -> a -> a -> Assertion

由于第二个参数“"The dog barks at midnight."”是一个字符串,我们知道a必须是String。因此do表达式

do 
  content <- hGetContents fileHandle
  return content

必须返回一个String。但是String = [Char],因此Haskell会将其视为List monad。我不知道为什么它推断[String]而不是[Char] = String,但这至少解释了List的来源。


BTW,Assertion已经是一个IO()。如果您return断言,您将获得IO(IO())。可能你的意思是

withTempFileContainingText :: (Handle -> IO c) -> IO c
withTempFileContainingText lambda = 
    bracketOnError 
        (return . snd =<< openTempFile "." "input.txt")
        (deleteFile)
        (\fileHandle -> do
            hPutStr fileHandle "The dog barks at midnight."
            hClose fileHandle
            lambda fileHandle)   -- # <--- No return here

然后testCase可以写成

testCase :: Assertion
testCase = withTempFileContainingText $ \fileHandle -> do
    conts <- hGetContents fileHandle
    assertEqual "contents as expected" "The dog barks at midnight." conts
    -- # ^-- No return here.

答案 2 :(得分:0)

感谢所有人提供了一些有价值的建议。

这会运行并通过:

withTempFileContainingText lambda = 
        bracketOnError 
            (openTempFile "." "input.txt")
            (\(filePath, fileHandle) -> do
                removeFile filePath)
            (\(filePath, fileHandle) -> do
                startOfFile <- hGetPosn fileHandle 
                hPutStr fileHandle "The dog barks at midnight."
                hFlush fileHandle
                hSetPosn startOfFile
                lambda fileHandle
                removeFile filePath)


testReadFile = runTestTT $ TestLabel "InteractionDomain.readFileContentsToList" (TestList [
    (TestLabel "testing infrastructure: read test file" (TestList [
        TestCase (withTempFileContainingText (\fileHandle -> do
            content <- hGetContents fileHandle 
            assertEqual "contents as expected" "The dog barks at midnight." content))]))])