Haskell类型误解分配变量

时间:2013-05-07 23:00:23

标签: haskell

在这里完成nok到haskell,我正在努力使这段代码工作:

它的目的是获取列表的第一个exp元素,连接它们,然后再次调用相同的函数。

order ( i ) (l1)(l2) =
        do exp <- (2 ^  i)
           l <- (take exp l1) ++ (take exp l2 ) ++ (order (i+1) (drop exp l1) (drop exp l2));
           return l

我确信这远不是惯用的哈斯克尔,但你必须在某处开始。

我得到的错误是

exp <- (2 ^ i ) 

No instance for (Num [Int])
      arising from a use of `^'
    Possible fix: add an instance declaration for (Num [Int])

我真的不确定这究竟意味着什么。是不是2和i都是整数,然后应用指数函数会产生整数?

谢谢!

3 个答案:

答案 0 :(得分:5)

我已按如下方式重写您的代码,并添加了一个主要内容。

order _ [] [] = []
order i l1 l2 =
                (take exp l1) ++ (take exp l2)
        ++      (order (i+1) (drop exp l1) (drop exp l2))
        where
        exp = 2^i

main = print $ order 1 [1,2,3,4] [3,4,5,6]

你犯的第一个错误就是你的递归没有终止,因为order总会再次自我调用。第二个错误是使用do,这引入了monad并且考虑到你是Haskell的新手,我会保持清醒一点。目前仅用于I / O.

我希望这会有所帮助。

P.S:您收到的错误消息是,Int列表以数字方式使用,并且没有默认实现。这可能是由monad列出的do引起的,但是我会把它留在Haskell的裂缝中给出一个确切的解释。

答案 1 :(得分:3)

do块中的所有语句必须属于同一个monad。这包括<-绑定的右侧。因此,因为第二个语句take exp l1 ++ ...的右侧是一个列表,编译器会推断2^i的类型也必须是一个列表。

这是因为<-不只是分配变量。在列表monad的情况下,它顺序地将左侧的变量绑定到右侧列表的每个元素。

如果您只想在do块中绑定变量而不产生任何其他影响,则应使用let绑定而不是<-

do let exp = 2^i
   l   <- take exp l1 ++ ... 
   return l

尽管如此,在这里使用do符号是多余的。 monad法律保证do x <- m; return xm相同,因此您可以直接将其写为

order i l1 l2 = take exp l1 ++ ...
  where exp = 2^i

答案 2 :(得分:2)

除了布莱恩的观点,我想我可以帮助解释你遇到这个特定错误的原因。

最重要的原因是exp <- 2 ^ i块中的do确实意味着“让exp成为2 ^ i值的名称“(你会在do块中将这个含义表示为let exp = 2 ^ i,但是do块实际上并不是你想要的那样。”

exp <- 2 ^ i的含义是“let exp是monadic值2 ^ i”产生的值的名称。尝试阅读<-,因为“来自”而不是“是”。究竟“来自”意味着什么取决于所涉及的单子。因此,对于这一行来说,2 ^ i必须是某种monad中的值。具体来说,它的类型类似Monad m => m a,适用于未知ma

由于^运算符适用于数值,因此会返回Num a => a类型的内容。因此,我们可以确定2 ^ i应该是类型为(Monad m, Num (m a)) => m a的内容,适用于未知ma

exp是从这个神秘的m a中提取出来的,所以它的类型为a。下一行包含take exp l1等表达式。 take要求其第一个参数属于Int类型,因此exp必须是Int类型,因此我们可以告诉那个未知的a我们与...合作必须Int。因此,2 ^ i现在已知为(Monad m, Num (m Int)) => m Int类型;它是某种一元整数。

在这一行中,您还有l <- (take exp l1) ++ ...。所以l也“来自”某种一元的价值。右侧可以看作是某种列表(由于使用了++takedrop)。参与do块的monad必须始终相同,并且列表类型构造函数确实是monad。因此,如果(take exp l1) ++ ...是某个列表,那么2 ^ i也必须是某个列表。

现在我们2 ^ i属于[Int]类型(我们原先知道它是m a; m是列表类型构造函数[],并且aInt)。但我们也知道(使用^运算符)它必须是Num类型类的成员。 <{1}}没有Num的实例,这正是你得到的错误。

这只是可以从您编写的代码中获得的许多不一致之一;这只是GHC在尝试分析时遇到的第一个。