单态限制打开*时,如何解决含糊不清的问题? [英] How to work around issue with ambiguity when monomorphic restriction turned *on*?
问题描述
因此,在学习Haskell时,我很快遇到了可怕的单态限制,并带有以下内容(以ghci为单位):
So, learning Haskell, I came across the dreaded monomorphic restriction, soon enough, with the following (in 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
因此,有很多关于此的材料,例如此处,解决这一问题并不难. 我可以为f添加显式类型签名,也可以关闭单态限制(直接在ghci或.ghci文件中使用:set -XNoMonomorphismRestriction".)
So there's a bunch of material about this, e.g. here, and it is not so hard to workaround. I can either add an explicit type signature for f, or I can turn off the monomorphic restriction (with ":set -XNoMonomorphismRestriction" directly in ghci, or in a .ghci file).
关于单态限制,有一些讨论,但似乎一般的建议是可以将其关闭(并且有人告诉我,在新版ghci中默认情况下实际上是关闭了).
There's some discussion about the monomorphic restriction, but it seems like the general advice is that it is ok to turn this off (and I was told that this is actually off by default in newer versions of ghci).
所以我关闭了它.
但是后来我遇到了另一个问题:
But then I came across another issue:
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"书中的示例代码简化的,该书对我不起作用,并且可以在以下页面上找到:
This is actually simplified from example code in the 'Real World Haskell' book, which wasn't working for me, and which you can find on this page: http://book.realworldhaskell.org/read/monads.html (it's the Monads chapter, and the getRandom example function, search for 'getRandom' on that page).
如果我将单态限制保留为 (或将其打开),则代码有效.如果我将其更改为,它也可以(启用了单态限制)
If I leave the monomorphic restriction on (or turn it on) then the code works. It also works (with the monomorphic restriction on) if I change it to:
Prelude> let (a,_) = System.Random.random (System.Random.mkStdGen 4) in a :: Int
-106546976
或者如果我之前指定了'a'的类型:
or if I specify the type of 'a' earlier:
Prelude> let (a::Int,g) = System.Random.random (System.Random.mkStdGen 4) in a :: Int
-106546976
但是,对于第二种解决方法,我必须打开作用域类型变量"扩展名(带有:set -XScopedTypeVariables").
but, for this second workaround, I have to turn on the 'scoped type variables' extension (with ":set -XScopedTypeVariables").
问题在于,在这种情况下(单态限制 on 时出现问题),两种变通办法似乎都不普遍适用.
The problem is that in this case (problems when monomorphic restriction on) neither of the workarounds seem generally applicable.
例如,也许我想编写一个函数来执行类似的操作并与任意(或多个)类型一起使用,并且在这种情况下,我很可能想做新的生成器状态(以"g"表示).
For example, maybe I want to write a function that does something like this and works with arbitrary (or multiple) types, and of course in this case I most probably do want to hold on to the new generator state (in 'g').
然后的问题是:通常如何在不直接指定确切类型的情况下如何解决此类问题?
The question is then: How do I work around this kind of issue, in general, and without specifying the exact type directly?
(作为Haskell的新手)也可以很好地了解此处到底发生了什么以及这些问题的发生原因.
And, it would also be great (as a Haskell novice) to get more of an idea about exactly what is going on here, and why these issues occur..
推荐答案
定义时
(a,g) = random (mkStdGen 4)
然后,即使g
本身始终是StdGen
类型,g
的 value 仍然取决于a
的 type ,因为类型不同它们使用随机数生成器的方式可能会有所不同.
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.
此外,稍后(假设)使用 g
时,只要a
最初是多态的,就无法确定哪种类型的<您要用于计算g
的c3>.
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
.
所以,单独地,作为多态定义,必须禁止上述内容,因为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.
这是带有let/where
绑定的一般问题,该绑定将模式中的多个变量绑定在一起,这可能是普通的单态性限制甚至比单变量方程式更严格地对待它们的原因:使用模式,您甚至不能禁用MR给出多态类型签名.
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.
当您改用_
时,只要GHC不会影响a
的计算,就可以不用担心这种歧义.可能它可以检测到g
在以前的版本中未使用,并对其进行了类似的处理,但显然没有.
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.
对于没有给出不必要的显式类型的变通办法,您可以尝试用总是单态的Haskell中的一种绑定方法替换let/where
.以下所有工作:
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
这篇关于单态限制打开*时,如何解决含糊不清的问题?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!