【问题标题】:Haskell error: Could not deduce (Random a0) arising fromHaskell 错误:无法推断(随机 a0)源于
【发布时间】:2017-02-22 00:29:30
【问题描述】:

我在玩随机函数,它给出了一个无限的随机值列表,如“Learn You a Haskell for Great Good”的第 9 章所示。代码如下:

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

为了记录随机生成器,我将randoms' 更改为以下内容:

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

它按预期工作。

然后我用列表理解的方式重写了它:

randoms''' :: (RandomGen g, Random a) => g -> [(a, g)]
randoms''' gen = random gen : [random gen' | (_, gen') <- randoms''' gen]

这一次,编译器给出了错误:Could not deduc (Random a0) generated from a use of 'randoms''''......类型变量'a0'不明确......

但是,如果我使用具体类型指定 randoms''' 的类型,例如,

randoms''' :: StdGen -> [(Int, StdGen)]
randoms''' gen = random gen : [random gen' | (_, gen') <- randoms''' gen]

它再次正常工作,并给出与randoms''完全相同的结果。

我想知道为什么类型推断适用于randoms'',但对于randoms''' 却失败了。谁能告诉我为什么这两个不相等以及如何修复randoms'''的代码?

此外,我尝试了具有类似结构的测试代码:

generate :: (Integral a, RealFrac b) => a -> (b,a)
generate m = let x = 1.2^^m in (x, ceiling x)

foo :: (Integral g, RealFrac a) => g -> [(a,g)]
foo gen = let (value, newGen) = generate gen
          in (value, newGen) : foo newGen

foo' :: (Integral g, RealFrac a) => g -> [(a, g)]
foo' gen = generate gen : [generate gen' | (_, gen') <- foo' gen]

foo'' :: (Integral g, RealFrac a) => g -> [(a, g)]
foo'' gen = [generate gen' | gen' <- gen : map snd (foo'' gen)]

事实证明 foofoo'foo'' 都可以正常工作。显然,这不是单态与多态的问题。这似乎是 random 特有的问题。

【问题讨论】:

  • 您丢弃了randoms''' ((_, gen') &lt;- ...) 生成的实际值,因此编译器不知道使用哪种类型实例化randoms''';它没有理由选择与输出相同的类型a,也没有任何其他特定类型,因此它是模棱两可的。在单态情况下,不可能实例化 - 自然它可能只会选择Int 用于递归情况。

标签: haskell


【解决方案1】:
(_, gen') <- randoms''' gen

这里有_ 的类型是什么?您可能认为它应该是randoms''' 的签名中给出的a。然而,这是任意的。可能是(),也可能是Char。一切都可以在这里工作,因为无论如何你都不使用它。我们可以强制它与random gen'具有相同的类型:

randoms''' gen = random gen : [random gen' `asTypeOf` x | x@(_, gen') <- randoms''' gen]

asTypeOf 是一个标准函数,基本上是const 的类型约束版本:

asTypeOf :: a -> a -> a
asTypeOf = const

通过这个函数,我们可以确定x 的类型,从而确定_ 的类型,因为random gen' 的类型是已知的。

【讨论】:

  • 您使用asTypeOf 的第一个解决方案解决了这个问题。但是,第二个解决方案randomList 会产生几乎完全相同的错误消息(如果类型签名设置为单态,它会再次起作用)。我仍然不明白为什么 _ 的类型是任意的,因为它没有被使用;无论如何,在单态情况下不存在这样的问题。也许单态和多态的类型推断机制有些不同?
  • @Oyu 我删除了错误的功能。我不知道为什么当我尝试过它时它会起作用。无论哪种方式,_ 都被“使用”了。它是random 调用结果的一部分。如果我们使用let (val, gen) = random (mkStdGen 0) :: (a, StdGen),我们最终会得到另一个生成器gen,具体取决于类型。对于a ~ Int,我们得到(9106162675347844341,1346387765 2103410263)。对于a ~ Char,我们得到('\589059',40014 40692)。毕竟,您必须经常调用next 才能在整个值范围内创建均匀分布。
  • 我已经尝试了一些 foo 代码,这些代码在我的原始帖子末尾重新编辑。你能告诉我一些你的想法吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多