我有一个函数main
,它有一个子函数menu
。在main
中,我加载文件,要求用户输入其名称,然后调用menu
以显示菜单。菜单中的每个操作完成后,我将再次调用menu
,直到满足退出条件。
我在将main
中加载的文件传递给menu
时遇到问题 - 我想对每个命令执行数据库操作。
这是我的代码到目前为止(我已经删除了无关的位):
main :: IO ()
main = do contents <- readFile "myfile.txt"
let finalDatabase = (read contents :: [Film])
putStr "Please enter your name: "
name <- getLine
menu finalDatabase
where menu db = do putStrLn "Please select an option:"
putStrLn "1: Display all films"
putStr "Choice: "
choice <- getLine
case choice of {
"1" -> displayAll db;
}
menu
displayAll
函数可以很好地打印出数据库。
我在WinHugs中遇到以下错误:
ERROR file:.\films.hs:152 - Type error in function binding
*** Term : menu
*** Type : [([Char],[[Char]],Int,[[Char]])] -> IO a
*** Does not match : IO a
我认为如果我没有指定像menu :: Database -> IO ()
这样的行,它会接受任何参数而不关心它们的类型。
有什么建议吗?
修改: 愚蠢的错误,我只是在案例陈述后再次调用菜单时忘了传递数据库!
答案 0 :(得分:1)
我认为如果我没有指定像
menu :: Database -> IO ()
这样的行,它会接受任何参数而不关心它们的类型。
这不是真的,在你的情况下menu
确实有类型Database -> IO ()
所以在where
之前的最后一行,你需要给它一个Database
来获取一个IO ()
- 可能是db
(如menu db
中所示),但也许稍后,您将开始修改数据库,然后您可以传入新数据库.Haskell不只是环顾四周在什么范围内找到Database
类型的合适值!
如果数据库确实已修复,则无需传入,您可以使用以下内容:
main = do contents <- readFile "myfile.txt"
let finalDatabase = (read contents :: [Film])
putStr "Please enter your name: "
name <- getLine
menu
where menu = do putStrLn "Please select an option:"
putStrLn "1: Display all films"
putStr "Choice: "
choice <- getLine
case choice of {
"1" -> displayAll finalDatabase -- instead of db
}
menu
但是,正如所说,当你开始修改'数据库'时,这将会破裂。假设您定义了一个函数doSomethingWithDatabaseDependingOnChoice :: String -> [Film] -> IO [Film]
,它接受选择和旧数据库,执行一些输入/输出并返回一个新数据库,您可以按如下方式使用它:
<same as before>
menu finalDatabase
where menu db = do putStrLn "Please select an option:"
putStrLn "1: Display all films"
putStr "Choice: "
choice <- getLine
newDb <- doSomethingWithDatabaseDependingOnChoice choice db
menu newDb
另一种选择是使用隐式参数,但我猜这一点有点太高级了!
答案 1 :(得分:0)
首先,不,不指定类型,并不意味着该函数将随时接受。这意味着将推断出类型。
您获得该特定类型错误的原因是,在menu
的{{1}}块结束时,您编写do
,这是一个函数而不是值类型为menu
。我不确定你为什么那样做。如果要创建无限循环,则应将最后一次调用更改为IO
,而不是返回未应用的函数。如果你不想要无限循环,我不明白为什么你最后会使用menu db
。