2

因此,在学习 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”)。

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

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”)。

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

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

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

而且,最好(作为 Haskell 新手)更了解这里到底发生了什么,以及为什么会出现这些问题。

4

1 回答 1

3

When you define

(a,g) = random (mkStdGen 4)

then even if g itself is always of type StdGen, the value of g depends on the type of a, because different types can differ in how much they use the random number generator.

Moreover, when you (hypothetically) use g later, as long as a was polymorphic originally, there is no way to decide which type of a you want to use for calculating g.

So, taken alone, as a polymorphic definition, the above has to be disallowed because g actually is extremely ambiguous and this ambiguity cannot be fixed at the use site.

This is a general kind of problem with let/where bindings that bind several variables in a pattern, and is probably the reason why the ordinary monomorphism restriction treats them even stricter than single variable equations: With a pattern, you cannot even disable the MR by giving a polymorphic type signature.

When you use _ instead, presumably GHC doesn't worry about this ambiguity as long as it doesn't affect the calculation of a. Possibly it could have detected that g is unused in the former version, and treated it similarly, but apparently it doesn't.

As for workarounds without giving unnecessary explicit types, you might instead try replacing let/where by one of the binding methods in Haskell which are always monomorphic. The following all work:

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
于 2015-03-22T08:53:21.457 回答