【问题标题】:Type of return in do blockdo 块中的返回类型
【发布时间】:2014-01-08 12:42:38
【问题描述】:

我试图理解 Haskell 中的 Monad,在我无数次代码实验中,我遇到了这个问题:

f2 = do   
  return "da"

以及它不想在编译时出现关于类型的巨大错误这一事实。我认为唯一重要的部分是:

 No instance for (Monad m0) arising from a use of return'
The type variable `m0' is ambiguous  

然后我将代码更改为:

 f2 = do   
 return "da"  :: IO [Char]  

而且效果非常好。但是当我试图搞砸一点并将类型更改为 IO Int 时,它又是一个错误。那么为什么类型实际上不是“模棱两可”呢? 另外,当我在返回之前添加一些内容时:

f2 = do   
  putStrLn "das"  
  return 2

那我就不用指定返回的类型了。 那么有人可以解释一下到底发生了什么吗?另外为什么在第一种情况下返回输出“da”?没有“”就不行吗?

【问题讨论】:

  • 感谢您编辑我的东西 jozefg 我自己都在努力做到这一点!
  • 没问题,欢迎使用stackoverflow和Haskell :)
  • 这接近于最小的可能类型错误消息,加上友好的编译器提供的一些额外建议。类型错误可能会变得更大!

标签: haskell types return monads


【解决方案1】:

在学习 monad 时,自己手动扩展它们会很有帮助,例如这个简单的例子:

test0 :: IO String
test0 = do
  a <- getLine
  putStrLn a
  return a

如果我们启用语言扩展{-# LANGUAGE ScopedTypeVariables #-},那么我们可以用显式类型注释 monad 中的每一行,这将显示返回块的类型。

{-# LANGUAGE ScopedTypeVariables #-}

test1 :: IO String
test1 = do
  a <- getLine             :: IO String
  putStrLn a               :: IO ()
  return a                 :: IO String

我们还可以注释左侧模式匹配的显式类型,它从右侧的 monad 上下文中“提取”。

test2 :: IO String
test2 = do
  (a :: String) <- getLine  :: IO String
  (() :: ()) <- putStrLn a  :: IO ()
  return a                  :: IO String

我们甚至可以将 do-notation 扩展到它的组成部分:

test3 :: IO String
test3 = getLine >>=
        (\a -> putStrLn a >>=
        \() -> return a)

希望这有助于建立你的单子直觉。

【讨论】:

    【解决方案2】:

    首先让我们指出这一点

    do
      return a
    

    完全一样

    return a
    

    现在,问题是return 具有类型

    return :: Monad m => a -> m a
    

    当你有这样的声明时

    foo = bar
    

    foo 没有参数,haskell 使它成为“单态”。这样做的结果是 Haskell 无法猜测 m 是什么并且不会概括它,因此您需要一个显式的类型签名。最普遍的是

    f2 :: Monad m => m String
    f2 = return "das"
    

    但您也可以使用 IO 或任何其他单子

    f2 :: IO String
    

    最后,在您的最后一个示例中,由于您要返回 2,因此您必须提供一个类型签名,表明您要返回某种数字,例如

     f2 :: IO Integer
    

    【讨论】:

    • 这忽略了为什么你不需要f2 的类型签名,即使它也是单态的。答案是putStrLn 将monad 限制为IO,所以最通用的类​​型是f2 :: Num n =&gt; IO n。这也不是单态的,但 Haskell 有 defaulting rules 用于数字类型,如果你不指定类型签名,这些会将 n 修复为 Integer
    【解决方案3】:

    这是已知的Monomorphism_restriction

    使用签名

    f2 :: Monad m => m String
    f2 = do   
      return "da"
    

    或使用语言扩展:

    {-# LANGUAGE NoMonomorphismRestriction #-}
    f2 = do   
      return "da"
    

    获取有效代码

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-03-07
      • 2010-10-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多