2013-02-14 185 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