haskell - 运行程序时出错
问题描述
我是 Haskell 的新手,我正在尝试创建一个程序来搜索目录并打印目录及其子目录中的文件列表。我一直在调试我的错误。我不知道出了什么问题,不幸的是,我在网上找不到有用的文档和各种教程辅助工具。
这是我想出的代码。但是,我不知道它是否有效,因为我无法通过调试错误。
import Control.Monad
import System.Directory
import System.FilePath
import System.Posix.Files
printDirectory :: FilePath -> IO [FilePath]
printDirectory = do
let filesInCurDir = getCurrentDirectory >>= getDirectoryContents
forM filesInCurDir $ \fry -> do
isDirectory <- doesDirectoryExist fry </>
if isDirectory
then do printDirectory
else putStrLn fry
putStrLn "Directory search completed"
return
以下是我的错误消息(对不起,有点冗长)。我意识到我的一些逻辑可能有点缺陷,尤其是 if 语句中的递归调用。不幸的是,我无法通过调试甚至开始修复逻辑。请有人可以帮助解释为什么我会收到我遇到的错误以及如何解决这些错误。
--错误信息--
ass3.hs:13:9: error:
• Couldn't match expected type ‘FilePath -> IO [FilePath]’
with actual type ‘[b0]’
• In a stmt of a 'do' block:
forM filesInCurDir
$ \ fry
-> do isDirectory <- doesDirectoryExist fry
</> if isDirectory then ... else putStrLn fry
putStrLn "Directory search completed"
In the expression:
do let filesInCurDir = getCurrentDirectory >>= getDirectoryContents
forM filesInCurDir $ \ fry -> do ...
return
In an equation for ‘printDirectory’:
printDirectory
= do let filesInCurDir = ...
forM filesInCurDir $ \ fry -> ...
return
|
13 | forM filesInCurDir $ \fry -> do
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...
ass3.hs:14:31: error:
• Couldn't match type ‘IO Bool’ with ‘[Char]’
Expected type: FilePath
Actual type: IO Bool
• In the first argument of ‘(</>)’, namely ‘doesDirectoryExist fry’
In a stmt of a 'do' block:
isDirectory <- doesDirectoryExist fry
</> if isDirectory then do printDirectory else putStrLn fry
In the expression:
do isDirectory <- doesDirectoryExist fry
</> if isDirectory then do printDirectory else putStrLn fry
putStrLn "Directory search completed"
|
14 | isDirectory <-doesDirectoryExist fry</>
| ^^^^^^^^^^^^^^^^^^^^^^
ass3.hs:14:50: error:
• Couldn't match type ‘[Char]’ with ‘Char’
Expected type: FilePath
Actual type: [FilePath]
• In the first argument of ‘doesDirectoryExist’, namely ‘fry’
In the first argument of ‘(</>)’, namely ‘doesDirectoryExist fry’
In a stmt of a 'do' block:
isDirectory <- doesDirectoryExist fry
</> if isDirectory then do printDirectory else putStrLn fry
|
14 | isDirectory <-doesDirectoryExist fry</>
| ^^^
ass3.hs:15:28: error:
• Couldn't match expected type ‘Bool’
with actual type ‘FileStatus -> Bool’
• Probable cause: ‘isDirectory’ is applied to too few arguments
In the expression: isDirectory
In the second argument of ‘(</>)’, namely
‘if isDirectory then do printDirectory else putStrLn fry’
In a stmt of a 'do' block:
isDirectory <- doesDirectoryExist fry
</> if isDirectory then do printDirectory else putStrLn fry
|
15 | if isDirectory
| ^^^^^^^^^^^
ass3.hs:16:41: error:
• Couldn't match type ‘FilePath -> IO [FilePath]’ with ‘[Char]’
Expected type: FilePath
Actual type: FilePath -> IO [FilePath]
• Probable cause: ‘printDirectory’ is applied to too few arguments
In a stmt of a 'do' block: printDirectory
In the expression: do printDirectory
In the second argument of ‘(</>)’, namely
‘if isDirectory then do printDirectory else putStrLn fry’
|
16 | then do printDirectory
| ^^^^^^^^^^^^^^
ass3.hs:17:30: error:
• Couldn't match type ‘IO ()’ with ‘[Char]’
Expected type: FilePath
Actual type: IO ()
• In the expression: putStrLn fry
In the second argument of ‘(</>)’, namely
‘if isDirectory then do printDirectory else putStrLn fry’
In a stmt of a 'do' block:
isDirectory <- doesDirectoryExist fry
</> if isDirectory then do printDirectory else putStrLn fry
|
17 | else putStrLn fry
| ^^^^^^^^^^^^
ass3.hs:17:39: error:
• Couldn't match type ‘[Char]’ with ‘Char’
Expected type: String
Actual type: [FilePath]
• In the first argument of ‘putStrLn’, namely ‘fry’
In the expression: putStrLn fry
In the second argument of ‘(</>)’, namely
‘if isDirectory then do printDirectory else putStrLn fry’
|
17 | else putStrLn fry
| ^^^
ass3.hs:18:17: error:
• Couldn't match type ‘IO’ with ‘[]’
Expected type: [()]
Actual type: IO ()
• In a stmt of a 'do' block: putStrLn "Directory search completed"
In the expression:
do isDirectory <- doesDirectoryExist fry
</> if isDirectory then do printDirectory else putStrLn fry
putStrLn "Directory search completed"
In the second argument of ‘($)’, namely
‘\ fry
-> do isDirectory <- doesDirectoryExist fry
</> if isDirectory then ... else putStrLn fry
putStrLn "Directory search completed"’
|
18 | putStrLn "Directory search completed"
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ass3.hs:19:9: error:
• Couldn't match expected type ‘[b0]’
with actual type ‘a0 -> m0 a0’
• Probable cause: ‘return’ is applied to too few arguments
In a stmt of a 'do' block: return
In the expression:
do let filesInCurDir = getCurrentDirectory >>= getDirectoryContents
forM filesInCurDir $ \ fry -> do ...
return
In an equation for ‘printDirectory’:
printDirectory
= do let filesInCurDir = ...
forM filesInCurDir $ \ fry -> ...
return
|
19 | return
| ^^^^^^
解决方案
是的,GHC 错误消息可能非常令人费解,但我会尝试通过这组内容与您交谈。第一个错误消息实际上是最难理解的,所以让我们跳到第二个。这个说:
- 当 GHC 查看 的第一个参数时
(</>)
,即表达式doesDirectoryExist fry
- 预计会找到 a
FilePath
(因为(</>)
运算符的第一个参数显然应该是 aFilePath
)- 但实际上它发现了一个
IO Bool
如果您检查 的类型doesDirectoryExist
,您可以看到——确实——它需要 aFilePath
并返回一个IO Bool
,所以 GHC 是正确的,你不能提供 a doesDirectoryExist fry
(它具有类型IO Bool
)作为某种FilePath
.
我不太确定您尝试与哪些路径组合(</>)
,但如果我们完全摆脱该运算符并重新格式化,以下看起来更像您的意图:
printDirectory :: FilePath -> IO [FilePath]
printDirectory = do
let filesInCurDir = getCurrentDirectory >>= getDirectoryContents
forM filesInCurDir $ \fry -> do
isDirectory <- doesDirectoryExist fry
if isDirectory
then do printDirectory
else putStrLn fry
putStrLn "Directory search completed"
return
如果你用这个版本重新编译,第一条错误信息会发生一些变化,但仍然令人困惑。但是,第二条错误消息已经消失,所以情况正在改善!!第三条错误消息(现在实际上是第二条错误消息)与以前相同。它说:
- 当 GHC 正在查看表达式时
fry
( 的第一个参数doesDirectoryExist
)- 它期望一个
FilePath
- 但它实际上发现了一个
[FilePath]
这很奇怪!我们也期望一个s FilePath
,而不是一个完整的FilePath
s 列表。那forM
是应该做的。这里发生的情况是其他一些不明显的错误导致 GHC 错误输入fry
为[FilePath]
而不是FilePath
. 为了解决这个问题,让我们通过使用语句覆盖fry
s 值来伪造它:let
printDirectory :: FilePath -> IO [FilePath]
printDirectory = do
let filesInCurDir = getCurrentDirectory >>= getDirectoryContents
forM filesInCurDir $ \fry -> do
let fry = "__FAKEFILEPATH__" -- DEBUGGING -- << CHANGE HERE
isDirectory <- doesDirectoryExist fry
if isDirectory
then do printDirectory
else putStrLn fry
putStrLn "Directory search completed"
return
如果我们重新编译,就会出现三个错误。第一个错误,一如既往的顽固,仍然令人困惑。第二条错误消息是原始列表中第五条消息的变体:
Directory.hs:13:18: error: • Couldn't match expected type ‘IO ()’ with actual type ‘FilePath -> IO [FilePath]’ • Probable cause: ‘printDirectory’ is applied to too few arguments In a stmt of a 'do' block: printDirectory In the expression: do printDirectory
在这里,GHC 认为表达式do printDirectory
应该有 type ,IO ()
但有 type FilePath -> IO [FilePath]
,这有助于表明您调用printDirectory
的参数太少(这是真的,因为printDirectory
需要文件路径)。让我们暂时提供fry
,即使我们稍后可能需要做一些不同的事情来获得正确的递归。
printDirectory :: FilePath -> IO [FilePath]
printDirectory = do
let filesInCurDir = getCurrentDirectory >>= getDirectoryContents
forM filesInCurDir $ \fry -> do
let fry = "__FAKEFILEPATH__" -- DEBUGGING
isDirectory <- doesDirectoryExist fry
if isDirectory
then do printDirectory fry -- FIXME -- << CHANGE HERE
else putStrLn fry
putStrLn "Directory search completed"
return
但是,这并不能真正清除错误。现在,GHC 告诉我们:
Directory.hs:14:15: error: • Couldn't match type ‘()’ with ‘[FilePath]’ Expected type: IO [FilePath] Actual type: IO () • In the expression: putStrLn fry In a stmt of a 'do' block: if isDirectory then do printDirectory fry else putStrLn fry
基本上,在 Haskell 中,语句的then
andelse
分支if
必须具有相同的类型,但是您试图在一个分支上返回文件列表(因为printDirectory
返回 type IO [FilePath]
),但在另一个分支上打印文件名(具有 type IO ()
) .
我想您必须在这里决定是要打印文件还是返回文件。你在你的问题中说你想打印它们,所以我猜你的printDirectory
签名是错误的。如果您只是打印,那么这是一个不返回任何内容(或至少没有任何有用的内容)的 IO 操作,因此签名应为:
printDirectory :: FilePath -> IO ()
如果你重新编译,你会得到两个错误。第一个与以前相同,第二个与原始列表中的最后一个错误相同:
Directory.hs:15:5: error: • Couldn't match expected type ‘IO b0’ with actual type ‘a0 -> m0 a0’ • Probable cause: ‘return’ is applied to too few arguments
你程序的最后一行似乎有一个奇怪的实际类型。值得庆幸的是,GHC 解释说您可能忘记向return
. 事实上,不清楚你想在这里返回什么(当你发布你的代码时,你似乎已经把它遗漏了,所以也许你已经决定删除这个return
)。无论如何,如果我们放弃它,我们只剩下一个错误:
Directory.hs:9:5: error: • Couldn't match expected type ‘FilePath -> IO ()’ with actual type ‘IO (IO ())’ • In a stmt of a 'do' block: forM filesInCurDir ...
在这里,GHC 决定forM ...
你的 do-block 中的语句应该有 type FilePath -> IO ()
,但它实际上有 type IO (IO b)
。
这令人困惑,但实际类型和预期类型都是错误的!该forM
语句应该是打印一堆文件路径的 IO 操作,所以它应该有 type IO ()
。
这就是发生的事情。在 Haskell 中,do-block 的类型是其最后一条语句的类型,而 GHC 以某种方式决定整个外部 do-block 应该具有类型FilePath -> IO ()
,这就是它希望最后一条语句具有该类型的原因。为什么它认为外部 do-block 应该有 typeFilePath -> IO ()
而不是IO ()
?好吧,因为你告诉它printDirectory
应该有类型FilePath -> IO ()
,然后直接将 do-block 绑定到printDirectory
而不给出printDirectory
参数。你需要这样写printDirectory dir = do ...
:
printDirectory :: FilePath -> IO ()
printDirectory dir = do -- << CHANGE HERE
let filesInCurDir = getCurrentDirectory >>= getDirectoryContents
forM filesInCurDir $ \fry -> do
let fry = "__FAKEFILEPATH__" -- DEBUGGING
isDirectory <- doesDirectoryExist fry
if isDirectory
then do printDirectory fry -- FIXME
else putStrLn fry
putStrLn "Directory search completed"
现在错误消息显示:
Directory.hs:9:5: error: • Couldn't match type ‘IO ()’ with ‘()’ Expected type: IO () Actual type: IO (IO ()) • In a stmt of a 'do' block: forM filesInCurDir ...
当您看到代码中出现IO xxx
vsIO (IO xxx)
不匹配时,通常是因为您将 do-block 语句编写为:
let x = something
什么时候应该是:
x <- something
在这里,如果我们检查getCurrentDirectory >>= getDirectoryContents
GHCi 中的类型,我们会看到它具有以下类型:
> :t getCurrentDirectory >>= getDirectoryContents
getCurrentDirectory >>= getDirectoryContents :: IO [FilePath]
所以返回文件路径列表是一个 IO 操作。但是,我们已将其分配给let
to filesInCurDir
。但我们不想filesInCurDir
成为一个 IO动作,我们希望它是实际的文件列表。为此,我们需要使用<-
代替let
:
printDirectory :: FilePath -> IO ()
printDirectory dir = do
filesInCurDir <- getCurrentDirectory >>= getDirectoryContents -- << CHANGE HERE
forM filesInCurDir $ \fry -> do
let fry = "__FAKEFILEPATH__" -- DEBUGGING
isDirectory <- doesDirectoryExist fry
if isDirectory
then do printDirectory fry -- FIXME
else putStrLn fry
putStrLn "Directory search completed"
现在,我们的语句仍然存在类型不匹配forM
,但我们越来越接近:
Directory.hs:9:5: error: • Couldn't match type ‘[()]’ with ‘()’ Expected type: IO () Actual type: IO [()] • In a stmt of a 'do' block: forM filesInCurDir
GHC 期望forM
具有类型IO ()
(即,执行一些打印并且不返回任何内容 AKA “unit” AKA 的操作),()
而是forM
尝试返回. 当您使用(它构建一个要返回的列表)代替(它只是针对其副作用运行一些 IO 操作,例如打印,但本身不返回任何东西)时,就会发生这种情况。因此,您需要替换为,您现在可以安全地删除“DEBUGGING”语句:()
forM
forM_
forM
forM_
printDirectory :: FilePath -> IO ()
printDirectory dir = do
filesInCurDir <- getCurrentDirectory >>= getDirectoryContents
forM_ filesInCurDir $ \fry -> do -- << CHANGE HERE
-- << REMOVE DEBUGGING
isDirectory <- doesDirectoryExist fry
if isDirectory
then do printDirectory fry -- FIXME
else putStrLn fry
putStrLn "Directory search completed"
它类型检查没有错误!
不幸的是,如果您尝试运行它,它只会进入无限循环,但那是因为递归被破坏了。目录内容包括您想要跳过的特殊条目"."
,".."
但即使您修复它,该函数实际上也不会更改当前目录,因此如果至少有一个子目录,它只会继续检查当前目录一遍又一遍。
所以,我想它仍然是调试时间!
推荐阅读
- opengl - 如何在OpenGL中正确渲染到两个四边形
- c# - 如何无休止地实例化一个游戏对象
- python - 无法从源解析导入“six.moves.urllib.parse”
- powershell - 如何通过 powershell 脚本传递参数
- angular - 如何在内部使用 nginx 并将 nginx 作为反向代理运行(多个)dockerized angular 应用程序?
- youtube-data-api - 使用 Youtube Data API v3 对播放列表中的视频进行分页时出错
- null - Oracle varchar2 字段空白与空
- html - 如何将内容属性中的文本放入图像的中间
- python - Tkinter 网格系统
- java - 中断线程B时如何识别线程A的名称或任何上下文信息?