繁体   English   中英

无法匹配预期类型 - Haskell代码

[英]Couldn't match expected type - Haskell Code

我正在尝试学习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)

谢谢,如果这不是提出这样一个问题的正确位置,请道歉。

您的代码存在几个问题,需要不同级别的工作才能修复。 按照我发现它们的顺序,你有......

类型不正确

很多类型签名都不正确。 如果函数完全执行任何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)

其中operator $表示“具有最低可能优先级的函数应用程序”,通常用于避免使用括号。

列表连接不正确

添加括号后,您的代码就行了

return (inFileArray:task)

其中inFileArray的类型为[String]task的类型为String 大概你打算在inFileArray的末尾添加task

:运算符用于将单个项添加到列表的前面 (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]

您需要做的是从createTask inFileArray的结果中打开IO层,并将生成的[String]传递给schedulerSimpleMain (它将其重新包装在IO层中)。 这正是operator >>= (发音为bind )的作用。 你可以把这一行写成

createTask inFileArray >>= schedulerSimpleMain

你可以将>>=运算符视为“管道转发”结果(有点像Unix管道运算符),但也可以在路上进行所有必要的展开/重新处理。

它可以是一个有点棘手正确使用绑定操作,当你刚刚起步的,这是我们提供的原因之一do摆在首位符号。 您可以将此代码段写为

do newInFileArray <- createTask inFileArray
   schedulerSimpleMain newInFileArray

这对于我上面编写的代码来说只是语法糖,但是如果你对bind运算符感到不舒服的话,它会更具可读性。

在第6行中,您有一个不同但相关的问题。 测序操作员; 本质上意味着“在左边做计算,忽略结果,然后在右边做计算”。 它要求左计算具有类型IO a ,而右计算具有类型IO b (对于任何ab )。

不幸的是,你正确的计算有[String] -> IO [String] ,所以这一行也不是类型检查。 要更正它,您只需确保将相应的参数提供给schedulerSimpleMain

do putStrLn "Invalid input."; schedulerSimpleMain inFileArray

现在哪个样子。 你的代码中都有这种错误。 我不会在这里详细介绍所有修复方法。 我想你应该先尝试自己解决。 如果你在一天左右的时间内仍遇到问题,我可以将更正的代码放在hpaste上供你学习。

我建议你用较小的位来打破你的程序并逐个测试它们。 我已经修复了你的几个功能:你可以为其他功能做同样的事情。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM