【问题标题】:"Couldn't match type ‘[]’ with ‘IO’" error in HaskellHaskell 中的“无法将类型‘[]’与‘IO’匹配”错误
【发布时间】:2019-09-23 23:59:47
【问题描述】:

我正在尝试使用 Points(我创建的一种数据类型)创建一个列表,其想法是在每次迭代中添加一个元素。出了点问题。

我试图将p 排除在myLoop 之外,但它似乎也不起作用。

main = myLoop 
myLoop  = do 
            let p = []
            done <- isEOF
            if done
              then putStrLn ""
              else do inp <- getLine
                      let (label:coord) = words inp
                      p ++  [Point label (map getFloat coord)]
                      -- print (pointerList)
                      myLoop 

我得到这个输出

trabalho.hs:30:23: error:
    • Couldn't match type ‘[]’ with ‘IO’
      Expected type: IO Point
        Actual type: [Point]
    • In a stmt of a 'do' block:
        p ++ [Point label (map getFloat coord)]
      In the expression:
        do inp <- getLine
           let (label : coord) = words inp
           p ++ [Point label (map getFloat coord)]
           myLoop
      In a stmt of a 'do' block:
        if done then
            putStrLn ""
        else
            do inp <- getLine
               let (label : coord) = ...
               p ++ [Point label (map getFloat coord)]
               ....
   |
30 |                       p ++  [Point label (map getFloat coord)]
   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

【问题讨论】:

  • Haskell 中的变量是不可变的。您不能在程序运行期间更改它们的值。请改用递归。
  • p ++ […] 不会改变 p。它创建一个新列表。而且因为这发生在返回类型为IOdo 块中,编译器抱怨它是一个列表而不是一个IO 操作。给myLoop 一个列表参数,最初使用main 中的空列表调用它,并在将新列表传递给递归调用之前附加到它。

标签: list haskell type-mismatch io-monad do-notation


【解决方案1】:

首先,do-notation 是语法糖,具有以下规则:

do x <- mA
   mB

大致* 对mA &gt;&gt;= \x -&gt; do mB 进行脱糖,其中mA 的类型为Monad m =&gt; m ado mB 的类型为Monad m =&gt; m b,某些类型为mab

do mA
   mB

去糖到mA &gt;&gt; do mB,其中mA 的类型为Monad m =&gt; m ado mB 的类型为Monad m =&gt; m b,对于某些类型mab

do a

去糖到a

main 是代表程序入口点的特殊名称,对于某些类型 a,它的类型为 IO a。因为您定义了main = myLoop,所以myLoop 也必须具有IO a 类型。

因此,在您的myLoop 函数中:

myLoop  = do 
            let p = []
            done <- isEOF
            if done
              then putStrLn ""
              else do inp <- getLine
                      let (label:coord) = words inp
                      p ++  [Point label (map getFloat coord)]
                      -- print (pointerList)
                      myLoop 

do-block 正在使用 m = IO 类型。因此,当您编写 p ++ [Point label (map getFloat coord)] 时,类型检查器需要 IO c 类型的值,对于某些类型 c

但是,p ++ [Point label (map getFloat coord)] 的类型为 [Point],导致类型错误 Cannot match type '[]' with 'IO'

正如类型不匹配所表明的那样,您的代码没有意义。我假设您想将Point label (map getFloat coord) 附加到p++ 不会 变异 p;它创建一个新列表!惯用的 Haskell 使用递归来实现你想要的。修复代码最直接的方法是执行以下操作:

main = myLoop []
myLoop p = do
    done <- isEOF
    if done
    then putStrLn ""
    else do inp <- getLine 
            let (label:coord) = words inp
            let p' = p ++  [Point label (map getFloat coord)]
            myLoop  p'

这里,myLoopp 作为参数,并在读取输入后递归地将更新后的p 传递给自身。 main 调用myLoop,参数为[],即p 的初始值。

Haskell Wikibook 有a good explanation of do-notation。一般来说,我建议阅读 Haskell Wikibook 以更好地理解 Haskell。

*我说“大致”是因为这些不是确切的规则。 Wikibook 文章深入解释了 do-notation。

【讨论】:

  • 修复它之后,也许评论一下p最终如何完全没有使用,并且修复那个,算法是二次的。可能的修复:利弊;使用差异列表((xs ++))编码而不是普通的++;或将do 块重组为非尾递归、sequence 类结构(getPoints =~ [ p:ps | p &lt;- getLine, ps &lt;- getPoints,为简洁起见,使用“monad comprehensions”编写)。
猜你喜欢
  • 2021-03-24
  • 2014-10-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多