我是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
的单独实例。有任何线索吗?
答案 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
的适当类型。无论您是否想要类型签名都是个人喜好,但对于您确定确实需要签名或实际需要签名的情况,了解发生的情况以及如何解决它仍然很有用。