简体   繁体   English

Haskell:实现“randoms”(又名,Ambiguous类型变量)

[英]Haskell: Implement “randoms” (a.k.a., Ambiguous type variable)

I am reading through LYAH, and in Chapter 9, I found a curious problem. 我正在阅读LYAH,在第9章中,我发现了一个奇怪的问题。 The author provides an example of implementing the "randoms" function: 作者提供了实现“randoms”功能的示例:

randoms' :: (RandomGen g, Random a) => g -> [a]
randoms' gen = let (value, newGen) = random gen in value:randoms' newGen

Well, this compiles just fine. 好吧,这个编译得很好。 But if I change the second line to: 但是,如果我将第二行更改为:

randoms' gen = (fst (random gen)) : (randoms' (snd (random gen)))

The this file reports error on loading: 此文件报告加载时出错:

IOlesson.hs:4:52:
    Ambiguous type variable `a' in the constraint:
      `Random a' arising from a use of `random' at IOlesson.hs:4:52-61
    Probable fix: add a type signature that fixes these type variable(s)
Failed, modules loaded: none.

If I change this line to: 如果我将此行更改为:

randoms' gen = (fst (random gen)) : (randoms' gen)

Then this will do just fine, and as expected, this will return a list of all identical elements. 然后这将做得很好,并且如预期的那样,这将返回所有相同元素的列表。

I am puzzled: What's so different in Miran's version and my version? 我很困惑:Miran的版本和我的版本有什么不同?

Thanks for any ideas! 谢谢你的任何想法!

The problem is that random takes any instance of RandGen , and returns a random value and a new generator of the same type. 问题是random接受RandGen任何实例,并返回一个随机值和一个相同类型的新生成器。 But the random value can be any type with an instance of Random ! 但是随机值可以是具有Random实例的任何类型!

random :: (Random a, RandomGen g) => g -> (a, g)

So, when you call random for the second time in the recursion, it doesn't know what the type of the first element should be! 所以,当你在递归中第二次调用random时,它不知道第一个元素的类型应该是什么! True, you don't really care about it (you throw it away with snd , after all), but the choice of a can affect the behaviour of random . 没错,你真的不关心它(你把它扔掉snd ,毕竟),而是一种选择能够影响的行为 random So to disambiguate, you need to tell GHC what you want a to be. 所以要消除歧义,你需要告诉GHC你想要是什么。 The easiest way is to rewrite your definition as follows: 最简单的方法是重写您的定义,如下所示:

randoms' gen = let (value, gen') = random gen in value : randoms' gen'

Because you use value as part of the resulting list, it's forced to have the same type as the a in your type signature — the element type of the resulting list. 因为您使用value作为结果列表的一部分,所以它被强制与类型签名中的a具有相同的类型 - 结果列表的元素类型。 The ambiguity is resolved, and the duplicate computation of the next random number is avoided, to boot. 解决了歧义,并且避免了下一个随机数的重复计算,以进行引导。 There are ways to disambiguate this more directly (keeping the duplicate computation), but they're either ugly and confusing or involve language extensions. 有一些方法可以更直接地消除歧义(保留重复计算),但它们要么丑陋又令人困惑或涉及语言扩展。 Thankfully, you shouldn't run into this very often, and when you do, a method like this should work to resolve the ambiguity. 值得庆幸的是,你不应该经常遇到这种情况,当你这样做时,这样的方法应该可以解决这种模糊性。

Equivalently and perhaps more neatly, you can write: 同样地,也许更整洁,你可以写:

randoms' gen = value : randoms' gen'
  where (value, gen') = random gen

Consider the type of random : 考虑random的类型:

random :: (RandomGen g, Random a) => g -> (a, g)

The result tuple consists of a value of any type that's an instance of Random , and the updated RNG value. 结果元组由任意类型的值组成,该类型是Random的实例,以及更新的RNG值。 The important part is the "any instance": nothing requires both uses of random to produce a value of the same type. 重要的部分是“任何实例”:没有任何需要使用random来产生相同类型的值。

In fst (random gen) there's no problem, because the value generated is whatever type the whole function needs; fst (random gen)中没有问题,因为生成的值是整个函数所需的任何类型; in snd (random gen) , however, the random value is thrown away, so it's completely unknown what type it should be. 然而,在snd (random gen) ,随机值被丢弃,因此完全不知道它应该是什么类型。 Without knowing the type, Haskell can't choose the appropriate Random instance to use, hence the ambiguous type error you see. 在不知道类型的情况下,Haskell无法选择合适的Random实例来使用,因此您看到的模糊类型错误。

random is of type: RandomGen g => g -> (a, g) random是这样的类型: RandomGen g => g -> (a, g)

and therefore snd (random gen) is only of type g -> g . 因此snd (random gen)只有g -> g类型。 And then it doesn't know what a is. 然后不知道a是。 There is a different random for each datatype you might want to generate, but in this case the compiler doesn't know whether you want a random :: g -> (Int,g) or a random :: g->(Char,g) or something else. 您可能想要生成的每种数据类型都有不同的random数,但在这种情况下,编译器不知道您是否需要random :: g -> (Int,g)random :: g->(Char,g)或其他东西。

This explains whey (value, newGen) = random gen works. 这解释了乳清(value, newGen) = random gen It helps the compiler to tie together it's knowledge of what a is. 它可以帮助编译器绑在一起是什么样的知识a是。 value must be of type a and hence it can deduce the type of random gen . value必须是a类型,因此它可以推断出random gen的类型。

( Edited : I deleted an incorrect attempt I made at fixing it. Just stick with the original code in the question!) 编辑 :我删除了我在修复它时所做的错误尝试。只需坚持问题中的原始代码!)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM