Haskell刚性类型变量错误

时间:2018-06-23 22:10:48

标签: haskell random polymorphism

我是Haskell初学者。我想知道为什么以下方法不起作用:

import System.Random

simulator :: (RandomGen g) => g -> Double -> (Bool, g)
simulator gen p = (x <= p, gen2)
    where (x, gen2) = random gen :: (Double, g)

我得到的错误是:

• Couldn't match type ‘g’ with ‘g1’
  ‘g’ is a rigid type variable bound by
    the type signature for:
      simulator :: forall g. RandomGen g => g -> Double -> (Bool, g)
    at simulate.hs:10:1-54
  ‘g1’ is a rigid type variable bound by
    an expression type signature:
      forall g1. (Double, g1)
    at simulate.hs:12:37-47
  Expected type: (Double, g1)
    Actual type: (Double, g)
• In the expression: random gen :: (Double, g)
  In a pattern binding: (x, gen2) = random gen :: (Double, g)
  In an equation for ‘simulator’:
      simulator gen p
        = (x <= p, gen2)
        where
            (x, gen2) = random gen :: (Double, g)
• Relevant bindings include
    gen :: g (bound at simulate.hs:11:11)
    simulator :: g -> Double -> (Bool, g) (bound at simulate.hs:11:1)

     where (x, gen2) = random gen :: (Double, g)

似乎Haskell无法匹配类型变量g的单独实例。有任何线索吗?

1 个答案:

答案 0 :(得分:3)

代码的问题在最后一行,即带有:: (Double, g)类型注释的代码。如所写,您显然希望注解中的g指向与g的类型签名中的simulator相同。这是一个完全合理的期望,但是不幸的是,事实并非如此-默认情况下,两个名称相同且类型注释不同的类型变量是不同的。 (这就是为什么GHC在错误消息中将您的第二个g隐式重命名为g1的原因。)

幸运的是,您可以解决此问题。 GHC带有一种语言扩展名,可以打开,名为ScopedTypeVariables。如果将{-# LANGUAGE ScopedTypeVariables #-}添加到模块顶部,将启用扩展名。但是,此 still 不会修复您的程序,因为ScopedTypeVariables仅适用于使用forall绑定的显式变量。因此,您需要同时添加LANGUAGE编译指示并引入对forall的显式使用:

{-# LANGUAGE ScopedTypeVariables #-}

import System.Random

simulator :: forall g. (RandomGen g) => g -> Double -> (Bool, g)
simulator gen p = (x <= p, gen2)
  where (x, gen2) = random gen :: (Double, g)

这些咒语足以使GHC达到您最初的意图。

也就是说,如果您只是删除第二个类型签名并禁用ScopedTypeVariables,您的原始程序实际上也会编译,因为GHC会根据其使用方式推断出gen2的适当类型。无论您是否想要类型签名都是个人喜好,但对于您确定确实需要签名或实际需要签名的情况,了解发生的情况以及如何解决它仍然很有用。