Haskell QuickCheck 在函数内生成值

Haskell QuickCheck generating values within a function

如何让这个人为的例子起作用?

newtype Q = Q [Int]

instance Arbitrary Q where
    arbitrary :: Gen Q
    arbitrary = do
        len <- choose (1, 5)
        pure $ g len (\ i -> i + choose (0, 1)) -- want different choice for each invocation

g :: Int -> (Int -> Int) -> Q -- g is a library function
g len f = Q $ fmap f [1 .. len]

它给出编译器错误:

    * Couldn't match expected type `Int' with actual type `Gen a0'
    * In the second argument of `(+)', namely `choose (0, 1)'
      In the expression: i + choose (0, 1)
      In the second argument of `g', namely `(\ i -> i + choose (0, 1))'

问题是 choose (0, 1) 不产生 Int,它产生 Gen Int

您将其视为此表达式中的 Int

pure $ g (\i -> i + choose (0, 1))

因为 Gen Int 是一个 monad 你需要绑定它才能使用 "choice".

的结果

像这样:

instance Arbitrary Q where
    arbitrary :: Gen Q
    arbitrary = do
        choice <- choose (0, 1)
        return $ g (\i -> i + choice)

回复编辑后的问题:

问题仍然存在,您正在尝试将 Gen Int 当作 Int 使用。 您可以在 do.

中多次绑定

这是一个解决方案:

instance Arbitrary Q where
    arbitrary :: Gen Q
    arbitrary = do
        len <- choose (1, 5)
        choice <- choose (0, 1)
        return $ g len (\i -> i + choice)

回复已编辑、已编辑的问题

你必须在某处传播副作用,这意味着你需要 运行 choose len 次。因为 g 是一个 "library" 函数,所以我假设您无法控制它,也无法更改它。请注意,下面的解决方案很丑陋,因为我需要使用部分函数 (!!),而且它相当慢(可能有更好的方法来执行此操作,但我找不到它)。

这里的诀窍是我正在映射一个 returns len Gen Int 的函数,然后 运行 所有这些函数,生成一个列表所选号码(有关详细信息,请参阅 mapM 说明)。

instance Arbitrary Q where
 arbitrary :: Gen Q
 arbitrary = do
     len <- choose (1, 5)
     choices <- mapM (\_ -> choose (0, 1)) [1 .. len]
     return $ g len (\i -> i + (choices !! i))