我正在尝试学习Haskell,但是我尝试编写的一小段示例代码遇到了相当大的“无法匹配预期类型”错误。任何人都可以给我一些指导,告诉我我做错了什么/我应该怎么做?
这些是错误,但我不确定我应该如何编写代码。
toDoSchedulerSimple.hs:6:14:
Couldn't match expected type `[t0]' with actual type `IO String'
In the return type of a call of `readFile'
In a stmt of a 'do' block: f <- readFile inFile
In the expression:
do { f <- readFile inFile;
lines f }
toDoSchedulerSimple.hs:27:9:
Couldn't match expected type `[a0]' with actual type `IO ()'
In the return type of a call of `putStr'
In a stmt of a 'do' block: putStr "Enter task name: "
In the expression:
do { putStr "Enter task name: ";
task <- getLine;
return inFileArray : task }
toDoSchedulerSimple.hs:34:9:
Couldn't match expected type `IO ()' with actual type `[a0]'
In a stmt of a 'do' block:
putStrLn "Your task is: " ++ (inFileArray !! i)
In the expression:
do { i <- randomRIO (0, (length inFileArray - 1));
putStrLn "Your task is: " ++ (inFileArray !! i) }
In an equation for `getTask':
getTask inFileArray
= do { i <- randomRIO (0, (length inFileArray - 1));
putStrLn "Your task is: " ++ (inFileArray !! i) }
toDoSchedulerSimple.hs:41:9:
Couldn't match expected type `[a0]' with actual type `IO ()'
In the return type of a call of `putStr'
In a stmt of a 'do' block:
putStr "Enter the task you would like to end: "
In the expression:
do { putStr "Enter the task you would like to end: ";
task <- getLine;
filter (endTaskCheck task) inFileArray }
toDoSchedulerSimple.hs:60:53:
Couldn't match expected type `IO ()'
with actual type `[String] -> IO ()'
In a stmt of a 'do' block: schedulerSimpleMain
In the expression:
do { (getTask inFileArray);
schedulerSimpleMain }
In a case alternative:
"get-task"
-> do { (getTask inFileArray);
schedulerSimpleMain }
这是代码本身。我认为这是相当简单的,但我的想法是通过调用其他函数来运行循环,接受输入并基于它执行操作。
import System.Random (randomRIO)
import Data.List (lines)
initializeFile :: [char] -> [String]
initializeFile inFile =
do f <- readFile inFile
let parsedFile = lines f
return parsedFile
displayHelp :: IO()
displayHelp =
do putStrLn "Welcome to To Do Scheduler Simple, written in Haskell."
putStrLn "Here are some commands you might find useful:"
putStrLn " 'help' : Display this menu."
putStrLn " 'quit' : Exit the program."
putStrLn " 'new-task' : Create a new task."
putStrLn " 'get-task' : Randomly select a task."
putStrLn " 'end-task' : Mark a task as finished."
putStrLn " 'view-tasks' : View all of your tasks."
quit :: IO()
quit =
do putStrLn "We're very sad to see you go...:("
putStrLn "Come back soon!"
createTask :: [String] -> [String]
createTask inFileArray =
do putStr "Enter task name: "
task <- getLine
return inFileArray:task
getTask :: [String] -> IO()
getTask inFileArray =
do i <- randomRIO (0, (length inFileArray - 1))
putStrLn "Your task is: " ++ (inFileArray !! i)
endTaskCheck :: String -> String -> Bool
endTaskCheck str1 str2 = str1 /= str2
endTask :: [String] -> [String]
endTask inFileArray =
do putStr "Enter the task you would like to end: "
task <- getLine
return filter (endTaskCheck task) inFileArray
viewTasks :: [String] -> IO()
viewTasks inFileArray =
case inFileArray of
[] -> do putStrLn "\nEnd of tasks."
_ -> do putStrLn (head inFileArray)
viewTasks (tail inFileArray)
schedulerSimpleMain :: [String] -> IO()
schedulerSimpleMain inFileArray =
do putStr "SchedulerSimple> "
input <- getLine
case input of
"help" -> displayHelp
"quit" -> quit
"new-task" -> schedulerSimpleMain (createTask inFileArray)
"get-task" -> do (getTask inFileArray); schedulerSimpleMain
"end-task" -> schedulerSimpleMain (endTask inFileArray)
"view-tasks" -> do (viewTasks inFileArray); schedulerSimpleMain
_ -> do putStrLn "Invalid input."; schedulerSimpleMain
main :: IO()
main =
do putStr "What is the name of the schedule? "
sName <- getLine
schedulerSimpleMain (initializeFile sName)
谢谢,如果这不是提出这样问题的正确位置,请道歉。
答案 0 :(得分:18)
您的代码存在多个问题,需要修改不同级别的工作。按照我发现它们的顺序,你有......
很多类型签名都不正确。如果函数完全执行任何I / O,则需要将其返回类型包装在IO
中。例如,而不是
createTask :: [String] -> [String]
你需要
createTask :: [String] -> IO [String]
反映了createTask
执行I / O的事实(它要求用户提供任务名称)。
幸运的是,修复此问题很简单 - 只需删除所有类型的签名即可!这听起来很疯狂,但它可能非常有用。 GHC具有强大的类型推断机制,这意味着通常可以在不明确指定类型的情况下推断类型。在您的程序中,所有类型很简单,可以推断,因此您可以删除所有类型的签名,在GHCi中加载模块并输入例如:t createTask
,然后解释器会告诉你推断的类型(然后你可以添加到源中)。
在Haskell中,函数应用程序具有最严格的绑定。特别是,当你写
putStrLn "Your task is: " ++ (inFileArray !! i)
这被Haskell解析为
(putStrLn "Your task is: ") ++ (inFileArray !! i)
不进行类型检查,因为左侧是IO ()
类型,右侧是String
类型。这也很容易修复。你只需要写出你想要的东西,这是
putStrLn ("Your task is: " ++ (inFileArray !! i))
或
putStrLn $ "Your task is: " ++ (inFileArray !! i)
其中运算符$
表示“具有最低可能优先级的函数应用程序”,通常用于避免使用括号。
添加括号后,您的代码有
行return (inFileArray:task)
其中inFileArray
的类型为[String]
,而task
的类型为String
。大概您打算将task
添加到inFileArray
的末尾。
:
运算符用于将单项添加到列表的前(O(1)操作)。您不能使用它将项添加到列表的末尾(O(n)操作)。 Haskell中的所有列表都是链表,因此在列表前面添加项目与将其添加到最后是完全不同的。你想要
return (task:inFileArray)
将任务添加到列表的前面,或
return (inFileArray ++ [task])
从task
创建一个新的单元素列表,并使用列表连接运算符++
将其添加到列表的末尾。
>>=
这是您的代码中最基本的误解,需要最多的工作来解释。让我们看一下以下(经过高度编辑的)代码片段:
schedulerSimpleMain :: [String] -> IO () -- 1
schedulerSimpleMain inFileArray = -- 2
do input <- getLine -- 3
case input of -- 4
"new-task" -> schedulerSimpleMain (createTask inFileArray) -- 5
_ -> do putStrLn "Invalid input."; schedulerSimpleMain -- 6
我们已经知道createTask
的类型是[String] -> IO [String]
。因此第5行不进行类型检查。函数schedulerSimpleMain
需要[String]
,但您传递IO [String]
。
您需要做的是从IO
的结果中打开createTask inFileArray
图层,并将生成的[String]
传递给schedulerSimpleMain
(将其重新包装在IO
中>>=
图层)。这正是运算符createTask inFileArray >>= schedulerSimpleMain
(发音为 bind )的作用。您可以将此行写为
>>=
您可以将do
运算符视为“管道转发”结果(有点像Unix管道运算符),但也可以在路上进行所有必要的展开/重新处理。
刚刚开始时正确使用绑定运算符可能有点棘手,这是我们首先提供do newInFileArray <- createTask inFileArray
schedulerSimpleMain newInFileArray
表示法的原因之一。您可以将此代码段写为
;
这只是我上面编写的代码的语法糖,但如果你对bind运算符不熟悉,它会更容易理解。
在第6行中,您有一个不同但相关的问题。排序运算符IO a
实质上意味着“在左边进行计算,忽略结果,然后在右边进行计算”。它要求左计算具有类型IO b
,并且正确计算要具有类型a
(对于任何b
和[String] -> IO [String]
)。
不幸的是,你的正确计算的类型为schedulerSimpleMain
,所以这一行也不会进行类型检查。要更正它,您只需确保将适当的参数提供给do putStrLn "Invalid input."; schedulerSimpleMain inFileArray
:
{{1}}
现在有什么样的。你的代码中都有这种错误。我不会在这里详细介绍所有修复方法。我想你应该先尝试自己解决。如果您在一天左右的时间内仍然遇到问题,我可以将更正的代码放在hpaste上供您学习。
答案 1 :(得分:1)
我建议你用较小的位来打破你的程序并逐个测试它们。 我已经修复了你的几个功能:你可以为其他功能做同样的事情。
import System.Random (randomRIO)
import Data.List (lines)
-- ERROR
-- f.hs:6:14:
-- Couldn't match expected type `[t0]' with actual type `IO String'
-- In the return type of a call of `readFile'
-- In a stmt of a 'do' block: f <- readFile inFile
-- In the expression:
-- do { f <- readFile inFile;
-- let parsedFile = lines f;
-- return parsedFile }
-- WHY?
-- initializeFile reads a file, therefore it must live inside the IO monad
initializeFile :: String -> IO [String]
initializeFile inFile = do
f <- readFile inFile
let parsedFile = lines f
return parsedFile
quit :: IO()
quit = do
putStrLn "We're very sad to see you go...:("
putStrLn "Come back soon!"
-- ERROR
-- f.hs:76:44:
-- Couldn't match expected type `IO ()'
-- with actual type `[String] -> IO ()'
-- In a stmt of a 'do' block: schedulerSimpleMain
-- In the expression:
-- do { putStrLn "Invalid input.";
-- schedulerSimpleMain }
-- In a case alternative:
-- _ -> do { putStrLn "Invalid input.";
-- schedulerSimpleMain }
-- WHY?
-- in the "_" case, schedulerSimpleMain is called without parameters, but
-- it needs a [String] one.
schedulerSimpleMain :: [String] -> IO()
schedulerSimpleMain inFileArray = do
putStr "SchedulerSimple> "
input <- getLine
case input of
"quit" -> quit
_ -> do putStrLn "Invalid input."; schedulerSimpleMain inFileArray
main :: IO()
main = do
putStr "What is the name of the schedule? "
sName <- getLine
-- Extract the lines from the IO monad
ls <- initializeFile sName
-- Feed them to the scheduler
schedulerSimpleMain ls