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

zw3*_*324 7 haskell types ghci type-variables

我正在阅读LYAH,在第9章中,我发现了一个奇怪的问题.作者提供了实现"randoms"功能的示例:

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

好吧,这个编译得很好.但是,如果我将第二行更改为:

randoms' gen = (fst (random gen)) : (randoms' (snd (random gen)))
Run Code Online (Sandbox Code Playgroud)

此文件报告加载时出错:

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.
Run Code Online (Sandbox Code Playgroud)

如果我将此行更改为:

randoms' gen = (fst (random gen)) : (randoms' gen)
Run Code Online (Sandbox Code Playgroud)

然后这将做得很好,并且如预期的那样,这将返回所有相同元素的列表.

我很困惑:Miran的版本和我的版本有什么不同?

谢谢你的任何想法!

ehi*_*ird 7

问题是random接受任何实例RandGen,并返回一个随机值和一个相同类型的新生成器.但随机值可以是任何类型的实例Random!

random :: (Random a, RandomGen g) => g -> (a, g)
Run Code Online (Sandbox Code Playgroud)

所以,当你random在递归中第二次调用时,它不知道第一个元素的类型应该是什么!没错,你真的不关心它(你把它扔掉用snd,毕竟),但选择一个可以影响行为random.所以要消除歧义,你需要告诉GHC你想要是什么.最简单的方法是重写您的定义,如下所示:

randoms' gen = let (value, gen') = random gen in value : randoms' gen'
Run Code Online (Sandbox Code Playgroud)

因为您使用value结果列表的一部分,所以它被强制与类型签名中的a具有相同的类型- 结果列表的元素类型.解决了歧义,并且避免了下一个随机数的重复计算,以进行引导.有一些方法可以更直接地消除歧义(保留重复计算),但它们要么丑陋又令人困惑或涉及语言扩展.值得庆幸的是,你不应该经常遇到这种情况,当你这样做时,这样的方法应该可以解决这种模糊性.

同样地,也许更整洁,你可以写:

randoms' gen = value : randoms' gen'
  where (value, gen') = random gen
Run Code Online (Sandbox Code Playgroud)