【问题标题】:How to work around issue with ambiguity when monomorphic restriction turned *on*?当单态限制*打开*时,如何解决歧义问题?
【发布时间】:2015-03-22 07:46:16
【问题描述】:

所以,在学习 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

所以有很多关于这个的材料,例如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”)。

如果我离开单态限制 on(或打开它),那么代码就可以工作。如果我将其更改为,它也可以(具有单态限制):

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

但是,对于第二种解决方法,我必须打开“作用域类型变量”扩展(使用“:set -XScopedTypeVariables”)。

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

例如,也许我想编写一个函数来做这样的事情并与任意(或多个)类型一起工作,当然在这种情况下我很可能确实想要坚持新的生成器状态(在“g”中)。

然后的问题是:我如何解决这类问题,一般来说,而不直接指定确切的类型?

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

【问题讨论】:

    标签: haskell monomorphism-restriction


    【解决方案1】:

    当你定义时

    (a,g) = random (mkStdGen 4)
    

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

    而且,当你(假设地)使用 g 之后,只要a 原本是多态的,就没有办法决定哪个 类型的@ 987654328@ 用于计算g

    因此,单独来看,作为多态定义,上述内容必须被禁止,因为 g 实际上 非常模棱两可,并且这种模棱两可无法在使用时修复网站。

    这是let/where 绑定在一个模式中绑定多个变量的一般问题,这可能是普通单态限制比单变量方程更严格地对待它们的原因:使用模式,你甚至不能禁用MR 通过给出多态类型签名。

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

    至于不提供不必要的显式类型的解决方法,您可以尝试用 Haskell 中的一种始终单态的绑定方法替换 let/where。以下所有工作:

    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
    

    【讨论】:

    • 正确。我有一个问题,你认为以下是 GHC 错误吗:x :: Random a =&gt; a; x = let (a, b) = random (mkStdGen 0) in a 类型检查有单态限制,但没有它就不会检查! (在 GHC 7.8.4 上)。 解除限制会导致更少程序检查,这对我来说真的很奇怪。
    • @AndrásKovács 在我开始写答案之前我认为这是一个错误,但现在我大约是 50/50:P
    猜你喜欢
    • 1970-01-01
    • 2022-08-14
    • 2011-05-04
    • 1970-01-01
    • 2022-01-20
    • 2017-10-03
    • 2022-01-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多