4

为什么 countInFile1 和 countInFile3 有编译器错误,而 countInFile0 和 countInFile2 没有。这四个都是一样的。

count :: String -> String -> Int
count w = length . filter (==w) . words

present :: String -> String -> IO String
present w = return . show . count w

-- VALID: pointed readFile, regular present
countInFile0 :: String -> FilePath -> IO ()
countInFile0 w f = putStrLn =<< present w =<< readFile f

-- INVALID: pointless readFile, regular present
countInFile1 :: String -> FilePath -> IO ()
countInFile1 w = putStrLn =<< present w =<< readFile

-- VALID: pointed readFile, inline present
countInFile2 :: String -> FilePath -> IO ()
countInFile2 w f = putStrLn =<< (return . show . count w) =<< readFile f

-- INVALID: pointless readFile, inline present
countInFile3 :: String -> FilePath -> IO ()
countInFile3 w = putStrLn =<< (return . show . count w) =<< readFile

main = do
  countInFile0 "bulldogs" "bulldogs.txt"
  countInFile1 "bulldogs" "bulldogs.txt"
  countInFile2 "bulldogs" "bulldogs.txt"
  countInFile3 "bulldogs" "bulldogs.txt"

还有为什么 countInFile3 有这个 countInFile1 没有的附加错误:

example_one.hs:21:27:
    No instance for (Monad ((->) FilePath))
      arising from a use of `=<<'
    Possible fix:
      add an instance declaration for (Monad ((->) FilePath))
    In the expression:
        putStrLn =<< (return . show . count w) =<< readFile
    In an equation for `countInFile3':
        countInFile3 w
          = putStrLn =<< (return . show . count w) =<< readFile
4

2 回答 2

9

使用countInFile1countInFile3,因为你正在组合形式的三个东西,所以a -> IO b你正在考虑所谓的 Kleisli 组合,即<=<from Control.Monad。尝试

 countInFile1 w = putStrLn <=< present w <=< readFile
 countInFile3 w = putStrLn <=< return . show . count w <=< readFile

或者你可以写countInFile3 w file = ... =<< readFile file,就像你在其他地方做的那样。readFile file(带参数)是一个IO String,所以它可以被传递>>=或传递=<<给任何String -> IO b。但这并不像你想要的那样时髦。 readFilejust 本身就是 aFilePath -> IO String所以它可以>=>用 anyString -> IO b来做 aFilePath -> IO b等等b -> IO c,在你的情况下以 a 结尾FilePath -> IO ()

第二个错误来自 ghc 试图读取=<< readFile,为此它需要readFilemb 用于某些 monad m,所以它会解决Monad ((->) FilePath)(这实际上对 是有意义的Control.Monad.Instances,但只会延迟获得第一个错误。)

如果您将file参数添加到这些参数中,它将是这样的,

 countInFile1 w file = (putStrLn <=< present w <=< readFile) file 

并且您可能正在以这种方式进行解析countInFile2countInFile0同时将它们解释=<<<=<实际上是这样的:

 countInFile0 w file = putStrLn =<< present w =<< (readFile file)

区别与之间的相同

 f n = (even . (+1) . (*3)) n

或等效地

 f   = even . (+1) . (3*)

另一方面

 f n = even $ (+1) $ 3 * n  -- cp. your 0 and 2

如果您n从此处删除双方

 f   = even $ (+1) $ (3*)  -- cp. your 1 and 3

您将收到类似于您看到的类型错误:

 No instance for (Integral (a0 -> a0)) arising from a use of `even'

你在哪里使用$你需要参数n——就像你在哪里使用>>=或者=<<你需要参数一样file。和.一样<=<,你没有。

于 2012-09-18T04:05:26.217 回答
8

函数应用的优先级高于中缀=<<运算符。

所以f =<< g a等价于f =<< (g a)而不是(f =<< g) a

于 2012-09-18T04:03:27.690 回答