Haskell QuickCheck генерирует значения внутри функции - PullRequest
0 голосов
/ 20 сентября 2019

Как мне заставить этот надуманный пример работать?

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))'

1 Ответ

3 голосов
/ 20 сентября 2019

Проблема в том, что choose (0, 1) не производит Int, оно производит Gen Int.

Вы рассматриваете это, как если бы это было Int в этом выражении:

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

Поскольку Gen Int является монадой, вам необходимо связать ее, чтобы использовать результат «выбора».

Примерно так:

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 является «библиотечной» функцией, я предполагаю, что вы не можете ее контролировать и не можете ее изменить.Обратите внимание, что приведенное ниже решение является уродливым, поскольку мне нужно использовать частичную функцию (!!), а также потому, что она довольно медленная (возможно, есть лучший способ сделать это, но я не могу ее найти).

Хитрость здесь в том, что я сопоставляю функцию, которая возвращает 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))
...