Haskell 刚性类型变量错误

Jer*_*ver 2 random polymorphism haskell

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

import System.Random

simulator :: (RandomGen g) => g -> Double -> (Bool, g)
simulator gen p = (x <= p, gen2)
    where (x, gen2) = random gen :: (Double, g)
Run Code Online (Sandbox Code Playgroud)

我得到的错误是:

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

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

Ale*_*ing 5

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

幸运的是,您可以解决此问题。GHC 附带一个语言扩展,您可以打开称为ScopedTypeVariables. 如果您添加{-# LANGUAGE ScopedTypeVariables #-}到模块的顶部,这将启用扩展。然而,这仍然不会解决你的程序,因为ScopedTypeVariables只适用于变量明确的使用结合forall。因此,您需要添加LANGUAGEpragma引入显式使用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)
Run Code Online (Sandbox Code Playgroud)

这些咒语足以让 GHC 执行您最初的意图。

也就是说,如果您只是删除第二个类型签名并保持ScopedTypeVariables禁用状态,您的原始程序实际上也可以编译,因为 GHC 会gen2根据它的使用方式推断适当的类型。您是否想要类型签名是个人偏好,但了解正在发生的事情以及如何在您决定确实需要签名或签名实际上需要的情况下修复它仍然很有用。