当*打开*单态限制时,如何解决歧义问题?

How to work around issue with ambiguity when monomorphic restriction turned *on*?

所以,学习 Haskell,我很快就遇到了可怕的单态限制,如下(在 ghci 中):

Prelude> let f = print.show
Prelude> f 5

<interactive>:3:3:
    No instance for (Num ()) arising from the literal `5'
    Possible fix: add an instance declaration for (Num ())
    In the first argument of `f', namely `5'
    In the expression: f 5
    In an equation for `it': it = f 5

关于这个有很多 material,例如here,解决方法并不难。 我可以为 f 添加显式类型签名,也可以关闭单态限制(直接在 ghci 或 .ghci 文件中使用“:set -XNoMonomorphismRestriction”)。

有一些关于单态限制的讨论,但似乎一般的建议是关闭它是可以的(我被告知在较新版本的 ghci 中这实际上是默认关闭的)。

所以我关闭了它。

但后来我遇到了另一个问题:

Prelude> :set -XNoMonomorphismRestriction
Prelude> let (a,g) = System.Random.random (System.Random.mkStdGen 4) in a :: Int 

<interactive>:4:5:
    No instance for (System.Random.Random t0)
      arising from the ambiguity check for `g'
    The type variable `t0' is ambiguous
    Possible fix: add a type signature that fixes these type variable(s)
    Note: there are several potential instances:
      instance System.Random.Random Bool -- Defined in `System.Random'
      instance System.Random.Random Foreign.C.Types.CChar
        -- Defined in `System.Random'
      instance System.Random.Random Foreign.C.Types.CDouble
        -- Defined in `System.Random'
      ...plus 33 others
    When checking that `g' has the inferred type `System.Random.StdGen'
    Probable cause: the inferred type is ambiguous
    In the expression:
      let (a, g) = System.Random.random (System.Random.mkStdGen 4)
      in a :: Int
    In an equation for `it':
        it
          = let (a, g) = System.Random.random (System.Random.mkStdGen 4)
            in a :: Int

这实际上是从 'Real World Haskell' 书中的示例代码简化而来的,它对我不起作用,您可以在此页面上找到它:http://book.realworldhaskell.org/read/monads.html(这是 Monads 章节,并且getRandom 示例函数,在该页面上搜索 'getRandom')。

如果我将单态限制 保留在 上(或将其打开),则代码可以正常工作。如果我将其更改为:

,它也可以工作(具有单态限制)
Prelude> let (a,_) = System.Random.random (System.Random.mkStdGen 4) in a :: Int 
-106546976

或者如果我之前指定了 'a' 的类型:

Prelude> let (a::Int,g) = System.Random.random (System.Random.mkStdGen 4) in a :: Int
-106546976

但是,对于第二种解决方法,我必须打开 'scoped type variables' 扩展(使用“:set -XScopedTypeVariables”)。

问题是在这种情况下(单态限制的问题)这两种解决方法似乎都不普遍适用。

例如,也许我想编写一个函数来执行类似这样的操作并处理任意(或多种)类型,当然在这种情况下我很可能 do想要保持新的生成器状态('g')。

接下来的问题是:一般情况下,我该如何解决此类问题,而不直接指定确切的类型?

而且,如果(作为 Haskell 新手)能够更多地了解这里到底发生了什么,以及为什么会出现这些问题,那也很好..

当你定义

(a,g) = random (mkStdGen 4)

那么即使 g 本身总是类型 StdGengvalue 取决于 type of a,因为不同的类型使用随机数生成器的程度可能不同。

而且,当你以后(假设)使用 g时,只要a本来就是多态的,就没办法决定您要使用哪种类型的a来计算g

所以,单独来看,作为一个多态定义,上面的定义是不允许的,因为 g 实际上 非常模棱两可的,而这种模棱两可 不能固定在使用地点。

这是 let/where 将多个变量绑定到一个模式中的绑定的常见问题,这可能是普通单态限制比单变量方程更严格地对待它们的原因:对于一个模式,您甚至不能通过提供多态类型签名来禁用 MR。

当你改用_时,想必GHC并不担心这种歧义,只要它不影响a的计算。可能它可以检测到 g 在以前的版本中未被使用,并以类似的方式处理它,但显然它没有。

至于不给出不必要的显式类型的变通方法,您可以尝试将 let/where 替换为 Haskell 中的一种绑定方法,这些方法 always 单态.以下所有工作:

case random (mkStdGen 4) of
    (a,g) -> a :: Int

(\(a,g) -> a :: Int) (random (mkStdGen 4))

do (a,g) <- return $ random (mkStdGen 4)
   return (a :: Int)    -- The result here gets wrapped in the Monad