Haskell错误:无法推断(Random a0)

时间:2020-03-02 16:53:56

标签: haskell typeerror

我正在尝试制作另一个Random实例,但由于类型错误而卡住了。我将其简化为以下ghci会话:

GHCi, version 8.6.5:
λ> import System.Random
λ> :t random
random :: (Random a, RandomGen g) => g -> (a, g)
λ> :t \g -> random g
\g -> random g :: (Random a, RandomGen g) => g -> (a, g)
λ> :t \g -> let xh = random g in xh
\g -> let xh = random g in xh
  :: (Random a, RandomGen g) => g -> (a, g)
λ> :t \g -> let (x, h) = random g in (x, h)

<interactive>:1:11: error:
    • Could not deduce (Random a0)
      from the context: (Random a, RandomGen b)
        bound by the inferred type of
                   it :: (Random a, RandomGen b) => b -> (a, b)
        at <interactive>:1:1
      The type variable ‘a0’ is ambiguous
    • When checking that the inferred type
        h :: forall a. Random a => b
      is as general as its inferred signature
        h :: b
      In the expression: let (x, h) = random g in (x, h)
      In the expression: \ g -> let (x, h) = ... in (x, h)

为什么在最新版本中失败?

1 个答案:

答案 0 :(得分:2)

这是一个可爱的问题。您可能会想像这会转换为显式词典传递样式,例如:

-- original
foo = \g -> let (x, h) = random g in (x, h)

-- assumed but incorrect explicit version
foo = /\a. /\b. \\(da :: Random a) => \\(db :: RandomGen b) =>
    \(g :: b) -> let tmp = random @a @b ,da ,db g
                     x = case tmp of (x, h) -> x
                     h = case tmp of (x, h) -> h
                 in (x, h)

在这里,我使用了一些自定义的新语法:

  • /\ty. tm是显式类型抽象;它采用一种类型,将其命名为ty,然后按术语tm进行命名
  • tm @ty是显式类型的应用程序;它使用一个多态项tm并提供ty作为第一类参数
  • \\(dict :: c) => tm是显式的类型类抽象;它使用一个类型类字典作为约束c的证据,将其命名为dict,然后像术语tm一样进行
  • tm ,dict是显式类型类应用程序;它使用一个假设某个类型类实例的术语,并提供字典dict作为该实例的证据

如果这是事物的编译方式,那么就不会有歧义,\g -> let (x, h) = random g in (x, h)的类型将与之前的所有术语相同。但这不是模式绑定实际编译的方式,因为这使得xh的类型比它们需要的类型更具限制性。而是以这种方式编译的:

-- actual explicit version
foo = /\a. /\b. /\c. \\(da :: Random a) => \\(db :: Random b) => \\(dc :: RandomGen c) =>
    \(g :: b) -> let x = /\d. \\(dd :: Random d) =>
                         case random @d @c ,dd ,dc g of (x, h) -> x
                     h = /\d. \\(dd :: Random d) =>
                         case random @d @c ,dd ,dc g of (x, h) -> h
                 in (x @a ,da , h @b ,db)

这是 more polymorphic ,因为在计算Random时用于选择x实例的类型与用于选择{{1} }计算Random时的实例。在这种情况下,这不是您想要的;但是有可能在其他情况下幻想这正是您想要的,并且将这两种类型捆绑在一起对于表达您想要的计算非常不便。

但是,由于此翻译所带来的额外灵活性,我们在这里遇到了一些束缚:事情的调用方如何影响为h选择哪种类型(因此用于b的字典,因为db不会出现在整体类型的任何位置!因此,编译器会为您提供类型歧义错误。作为一种直观的解释:我们知道要使用哪个b实例来计算Random,因为调用者必须为元组的第一部分选择一个类型;但是我们不知道该使用哪个实例来计算x,因为为h选择一种类型并不能确定用于丢弃的h的一半计算类型定义x

您可以要求编译器通过启用h扩展名来选择第一个翻译。您可以详细了解here。有时它会阻止MonoLocalBinds绑定以上述方式变为多态,这意味着类型推断将具有更多线索,但是某些其他可以接受的程序将不再进行类型检查。

相关问题