Reputation: 77
How do I get this contrived example to work?
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]
It gives compiler error:
* 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))'
Upvotes: 0
Views: 202
Reputation: 4318
The problem is choose (0, 1)
does not produce an Int
, it produces a Gen Int
.
You're treating it as though it were an Int
in this expression:
pure $ g (\i -> i + choose (0, 1))
Since Gen Int
is a monad you need to bind it in order to use the result of the "choice".
Something like this:
instance Arbitrary Q where
arbitrary :: Gen Q
arbitrary = do
choice <- choose (0, 1)
return $ g (\i -> i + choice)
Responding to the edited question:
The issue is still the same, you're trying to use the Gen Int
as though it were an Int
.
You can bind multiple times inside a do
.
Here is a solution:
instance Arbitrary Q where
arbitrary :: Gen Q
arbitrary = do
len <- choose (1, 5)
choice <- choose (0, 1)
return $ g len (\i -> i + choice)
Responding to the edited, edited question:
You have to propagate the side effects somewhere, this just means you need to run choose
len
times. Because g
is a "library" function, I'm going to assume that you have no control over it, and can't change it. Note that the solution below is ugly since I need to use the partial function (!!)
, and because it is rather slow (there is probably a better way to do this, but I'm not able to find it).
The trick here is I'm mapping a function that returns len
Gen Int
's, and then runs all of them, producing a list of chosen numbers (see mapM
description for more details).
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))
Upvotes: 4