Consider the problem of generating strings out our a set of possible strings, in such a way that once a string is chosen, it cannot be repeated again. For this task I would like to use QuickCheck
's Gen
functions.
If I look at the type of the function I'm trying to write, it looks pretty much like a state monad. Since I'm using another monad, namely Gen
, inside the state monad. I wrote my first attempt using StateT
.
arbitraryStringS :: StateT GenState Gen String
arbitraryStringS =
mapStateT stringGenS get
where:
newtype GenState = St {getStrings :: [String]}
deriving (Show)
removeString :: String -> GenState -> GenState
removeString str (St xs) = St $ delete str xs
stringGenS :: Gen (a, GenState) -> Gen (String, GenState)
stringGenS genStSt =
genStSt >>= \(_, st) ->
elements (getStrings st) >>= \str ->
return (str, removeString str st)
Something that troubles me about this implementation is the fact that I'm not using the first element of stringGenS
. Secondly, my end goal is to define a random generator for JSON values, that make use of a resource pool (which contains not only strings). Using StateT
led me to implement "stateful" variants of QuickCheck
's elements
, listOf
, etc.
I was wondering whether there's a better way of achieving this, or such a complexity is inherent to defining stateful variants of existing monads.