2013-02-14 149 views
2

过去几天我一直在试图学习Haskell。虽然我正在慢慢变好,但我发现很难用Haskell的IO进行推理,可能是由于我缺乏知识。我一直在努力编写一个简单的待办事项列表程序。下面是我得到了什么:Haskell类型错误消息

tadd todo = do 
    td <- getLine 
    td:todo 

tdel todo = do 
    trem <- getLine 
    let rid = read trem :: Int 
    [todo !! x | x <- [0..(length todo-1)], not $ x == rid] 

tls todo = do 
    mapM putStrLn [ (show x) ++ (todo !! x) | x <- [0..(length todo -1)] ] 
    todo 

mtodo "add" todo = tadd todo 
mtodo "del" todo = tdel todo 
mtodo "ls" todo = tls todo 


bege = do 
    com <- getLine 
    mtodo com [] 

main = bege 

我的除外mtodo是mtodo :: [IO String] -> [IO String] -> [IO String] 和TADD,TDEL,TLS是:: [IO String] -> [IO String]

相反,我刚刚得到这个可怕的错误讯息话题

[1 of 1] Compiling Main    (todo.hs, todo.o) 

todo.hs:3:9: 
    Couldn't match type `[]' with `IO' 
    Expected type: IO String 
     Actual type: [String] 
    In a stmt of a 'do' block: td : todo 
    In the expression: 
     do { td <- getLine; 
      td : todo } 
    In an equation for `tadd': 
     tadd todo 
      = do { td <- getLine; 
       td : todo } 

todo.hs:8:9: 
    Couldn't match expected type `IO' with actual type `[]' 
    In a stmt of a 'do' block: 
     [todo !! x | x <- [0 .. (length todo - 1)], not $ x == rid] 
    In the expression: 
     do { trem <- getLine; 
      let rid = ...; 
      [todo !! x | x <- [0 .. (length todo - 1)], not $ x == rid] } 
    In an equation for `tdel': 
     tdel todo 
      = do { trem <- getLine; 
       let rid = ...; 
       [todo !! x | x <- [0 .. (length todo - 1)], not $ x == rid] } 

todo.hs:12:9: 
    Couldn't match type `[]' with `IO' 
    Expected type: IO [Char] 
     Actual type: [[Char]] 
    In a stmt of a 'do' block: todo 
    In the expression: 
     do { mapM 
      putStrLn [(show x) ++ (todo !! x) | x <- [0 .. (length todo - 1)]]; 
      todo } 
    In an equation for `tls': 
     tls todo 
      = do { mapM 
        putStrLn [(show x) ++ (todo !! x) | x <- [0 .. (length todo - 1)]]; 
       todo } 

任何想法有什么错我的类型? (另外 - 有什么我应该改变?)。由于

+1

回报是你的朋友 – zurgl 2013-02-14 15:11:07

+5

明确键入东西!当你感到困惑时,它会让你更容易推理,因为它会迫使你问自己这样的问题,例如*我想让它返回哪种类型?*和*这是什么东西?*。 – gspr 2013-02-14 15:13:03

+0

顺便说一下,通常最好使用空格代替Haskell代码的制表符,而不仅仅是因为它适用于复制粘贴到SO问题。 :] Haskell关心对齐,而不仅仅是缩进,并且标签的解释方式可能与您的编辑器显示它们的方式不匹配。 – 2013-02-14 15:37:35

回答

10

看这段代码:

tadd todo = do 
    td <- getLine 
    td:todo 

的问题是,你有td:todo就其本身的线路;你正在使用getLine,所以整个块应该使用IO,但最后一行是一个列表。这就是“无法匹配”的错误所在 - 代码似乎认为IO[]应该是相同的东西,但它们当然不是。

解除一个值到一元上下文以便将其用作do块的结果值,使用该函数return

tadd todo = do 
    td <- getLine 
    return $ td:todo 

这会给tadd类型[String] -> IO [String]。这不是你期待的类型,但是从你写的内容来看,我不认为你想要使用[IO String]

这同样适用于tdel中的列表理解和tls中的最终todo

此外,您预计类型这里是关:

mtodo :: [IO String] -> [IO String] -> [IO String] 
mtodo "add" todo = tadd todo 
mtodo "del" todo = tdel todo 
mtodo "ls" todo = tls todo 

第一个参数显然是一个String在这里,所以基于上述,你可能想mtodo :: String -> [String] -> IO [String]

这大概也是不是你想要的:

bege = do 
    com <- getLine 
    mtodo com [] 

其他一些注意事项:

基于其他代码,您似乎需要一个交互式程序,但这只会运行一次。你可能就要什么是这样的:

bege todo = do 
    com <- getLine 
    todo' <- mtodo com todo 
    bege todo' 

main = bege [] 

你的列表中理解[todo !! x | x <- [0..(length todo-1)], not $ x == rid]是非常低效的,而不是惯用的。作为一个经验法则,如果你仍然在学习Haskell,你应该永远不要使用(!!),除非你之后丢弃了这个列表。哈斯克尔列表是线性序列,所以索引需要遍历所有点。并不是真正的最佳数据结构,但你至少应该找到一种方法来做到这一点,它不需要多次遍历列表。

作为避免(!!)的例子,你可以重写mapM putStrLn [ (show x) ++ (todo !! x) | x <- [0..(length todo -1)] ]mapM_ putStrLn $ zipWith (\n t -> show n ++ t) [0..] todo,避免了像索引所有混乱和多余的东西,担心列表的长度,以及遍历列表只有一次,而不是穿越它的首先要计算长度,然后部分遍历每个项目以打印它。

这更多的是品味的问题,但不是写这个:

mtodo "add" todo = tadd todo 
mtodo "del" todo = tdel todo 
mtodo "ls" todo = tls todo 

你可以缩写它:

mtodo "add" = tadd 
mtodo "del" = tdel 
mtodo "ls" = tls 

...这意味着同样的事情,而且是通过减少多余的噪音并明确地表明它正在做的是根据给定的字符串选择几个其他功能之一来证明更清楚。

此外,let rid = read trem :: Int位是不必要的 - 只需使用功能readLn而不是getLine,它完全符合您的要求。此外,类型注释可能是不必要的(并且分散注意力),因为类型应该根据您对结果做什么来推断。

+0

这确实是互动的..我只是删除了我的尝试,因为它没有它们就已经够了。谢谢 – mgoszcz2 2013-02-14 18:47:15

1

您需要了解退货。所有你需要做的事情是让tadd,tdel和tls的最后一行返回。

如果您使用显式类型签名,则会得到更好的错误消息。

tadd :: [String] -> IO [String] 
tadd todo = do 
    td <- getLine 
    return (td:todo) 

在这种情况下,(td:todo)具有类型[String],你需要添加回把它纳入IO单子,这是函数需要返回什么。当你使用符号时,每行需要在你所在的monad中有一个类型(除了以let开头的那个),在这个例子中就是IO。

tdel和tls有完全相同的问题。

tdel :: [String] -> IO [String] 
tdel todo = do 
    trem <- getLine 
    let rid = read trem :: Int 
    return [todo !! x | x <- [0..(length todo-1)], not $ x == rid] 

tls :: [String] -> IO [String] 
tls todo = do 
    mapM putStrLn [ (show x) ++ (todo !! x) | x <- [0..(length todo -1)] ] 
    return todo