使用`choose`时,在自定义类型类上没有System.Random.Random的实例

时间:2018-10-23 03:22:07

标签: haskell quickcheck

我正在根据第一原理在Haskell编程中进行练习。它要求我分别产生相等的概率,以及每个概率分别产生的1 / 3、2 / 3概率:

data Fool =
  Fulse
  | Frue
  deriving (Eq, Show)

我的答案是

module Random where

-- import Test.Hspec
import Test.QuickCheck

data Fool =
  Fulse
  | Frue
  deriving (Eq, Show)


genFool :: Gen Fool
genFool = choose (Fulse, Frue)

genFool' :: Gen Fool
genFool' = do
  frequency [(2, return Fulse)
            ,(1, return Frue)]

但是genFool是错误的。错误消息是:

../chap14/random.hs:13:11: error:
    • No instance for (System.Random.Random Fool)
        arising from a use of ‘choose’
    • In the expression: choose (Fulse, Frue)
      In an equation for ‘genFool’: genFool = choose (Fulse, Frue)
   |
13 | genFool = choose (Fulse, Frue)
   |           ^^^^^^^^^^^^^^^^^^^^

以前我有一些这样的代码:

genBool :: Gen Bool
genBool = choose (False, True)

正常工作。我认为可能需要instance中的一些预定义System.Random.Random Fool才能使choose工作。

我该怎么做才能编译我的genFool版本?

还有,为什么return Fulse位于类型genFool'的第二个Gen Fool中?

1 个答案:

答案 0 :(得分:0)

Test.Quickcheck模块的choose函数具有以下类型签名:

choose :: Random a => (a, a) -> Gen a 

因此,如果您打算在choose类型上使用Fool函数,则必须使其成为Random类型类的实例,如上面的类型签名所示。

这是定义Random typeclass的地方。它需要randomRrandom的最小实现。

由于类型Fool只有两个值,因此它与Bool类型同构。因此,您可以定义一个适当映射值的函数:

mapBool :: Bool -> Fool
mapBool False = Fulse
mapBool True = Frue

然后您可以为您的类型定义一个typeclass实例:

instance Random Fool where
    random g = let (b :: Bool, g') = random g
               in (mapBool b, g')
    randomR _ g = (random g) -- Note that this doesn't work correctly. You need to pattern match and fix this.

上面的代码应该可以使您的模块正常编译。