“没有实例(Monad ......”在if-then-else和后卫中

时间:2012-02-14 11:15:09

标签: haskell

我有以下功能:

appendMsg :: String -> (String, Integer) -> Map.Map (String, Integer) [String] -> Map.Map (String, Integer) [String]
appendMsg  a (b,c) m = do
      let Just l = length . concat <$> Map.lookup (b,c) m
          l' = l + length a
      --guard $ l' < 1400
      --return $ Map.adjust (++ [a]) (b, c) m
      if l' < 1400 then let m2 = Map.adjust (++ [a]) (b, c) m in return m2 else return (m)

如果l'&lt; 1400,在l'> 1的情况下,应创建并返回值m2。 1400我最终想要调用第二个函数,但在这种情况下,现在只返回m或者m就足够了。

我从警卫开始,并立即在错误中运行

No instance for (Monad (Map.Map (String, Integer)))
      arising from a use of `return'

然后我尝试了if-then-else,不得不解决一些误解,并最终得到了同样的错误。

我想知道如何解决这个问题。我确实理解Monad是什么,或者Haskell中的数据是不可变的。然而,在阅读了两本关于Haskell的书之后,我觉得还有很远的地方可以编写一些有用的东西。总有一个Monad ......:D。

2 个答案:

答案 0 :(得分:4)

您声明您的功能结果是

Map.Map (String, Integer) [String]

并且,通过使用return,声明它也是monadic。因此,编译器认为Monad应该有一个Map (String, Integer)实例。

但是我很确定如果你放弃returndo并在in之前写if,这将会编译。或者至少它会给你更有意义的错误信息。

答案 1 :(得分:4)

这看起来非常像你对return所做的事感到困惑。与许多命令式语言不同,return 就像从函数返回值一样。

在Haskell中,您可以通过说f a b c = some_expression之类的内容来定义函数。右侧函数“返回”的表达式。你不需要在命令式语言中使用“返回语句”,事实上它没有意义。在命令式语言中,函数是一系列执行的语句,它很自然地适用于使用 return语句来确定函数的结果,该语句终止函数并传递一个值。在Haskell中,函数的右侧是一个单独的表达式,并且突然在该表达式的中间放置一个语句,以某种方式终止对表达式的求值并返回其他内容并不是真的有意义。

实际上,return本身就是一个函数,而不是一个语句。它的命名可能很差,因为它不是那些学习Haskell的命令式程序员所期望的。 return foo是将foo包装成monadic值的方式;它对流控制没有任何影响,但只是在你正在使用的monad的上下文中求值为foo。看起来你根本就没有使用monad(如果你的话,你的函数的类型肯定是错的。)

值得注意的是,如果/ then / else也不是Haskall中的声明;整个if / then / else块是一个表达式,它根据条件的值计算then表达式或else表达式。

所以带走do,它只是提供了使用monad的方便语法。然后,您不需要return任何内容,只需将m2作为then分支(使用let绑定,或者只使用m2一次您可以在then分支中用Map.adjust (++ [a]) (b, c) m)和m替换整个else表达式。然后,将其包装在let ... in ...块中,以获得ll'的绑定。整个let ... in ...构造也是一个表达式,因此它可以就像你的函数定义的右边一样。没有do,没有return