You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I have a test pattern that looks like: when I generate objects, put them in state; then when I generate other objects that reference them, I can look them up. So if a File references a Folder, and I want to create a File, I have two options:
Generate a Folder explicitly and pass it to the File generator
Look up the list of Folders in state and either pick one of them or generate a new one using some default generator
This makes it easy to say "give me three different Files, which may or may not be in the same Folder".
But, it seems that shrinking doesn't work like I'd hoped. Here's a simple demonstration:
numGen1::StateT [Int] Gen (Int, [Int])
numGen1 =do
a <-do
num <-Gen.int (Range.constant 010)
State.modify (num :)
pure num
b <-do
num <-Gen.int (Range.constant 010)
State.modify (num :)
pure num
numsState <-State.get
(, [b, a]) <$>Gen.element numsState
numGen2::StateT [Int] Gen (Int, [Int])
numGen2 =do
numsGen <-Gen.list (Range.constant 22) $do
num <-Gen.int (Range.constant 010)
State.modify (num :)
pure num
numsState <-State.get
(, numsGen) <$>Gen.element numsState
I'd hope these would be basically the same: generate two numbers, save them in state, then pick one of the numbers that was saved. We have runTateT numGen_ [] :: Gen ((Int, [Int]), [Int])) and for every value generated, the two lists should be equal and the single value should be contained in them.
This property holds for the immediately generated values, and for shrunk values from numGen1:
where the two lists are different, though somehow the single value is still contained in both of them.
I think the culprit is that Gen.list does something complicated:
list::MonadGenm=>RangeInt->ma->m [a]
list range gen =let
interleave =
(interleaveTreeT . nodeValue =<<)
in
sized $\size ->
ensure (atLeast $Range.lowerBound size range) .
withGenT (mapGenT (TreeT. interleave . runTreeT)) $do
n <- integral_ range
replicateM n (toTreeMaybeT gen)
interleaveTreeT::Monadm=> [TreeTma] ->m (NodeTm [a])
interleaveTreeT =fmapTree.interleave .traverse runTreeT
it's not clear to me why it does this, but the term "interleave" makes me think it's about rearranging the shrink tree, where the default behavior would only shrink one element at a time?
(If we replace Gen.list (Range.constant 2 2) with replicateM 2, then numGen2 behaves like numGen1.)
So I guess I'm asking if this kind of thing is expected behavior for StateT s Gen a; and is there a way to do the kind of thing I'm trying to do without avoiding Gen.list entirely?
I've wondered about using GenT (State s) a instead, but I don't know how that would work. There's hoist to turn it into a Gen a, and hoist (Identity . flip evalState []) typechecks. Does it work? It's not obviously wrong, I can simply change the type of numGen2 to GenT (State [Int]) (Int, [Int]) and do
...but I've lost access to the state variables here, so I can't tell what's going on with that, and passing in [] feels like I might be losing state somewhere? But I don't know. Similarly I could use forAllT, but then I'd need to turn a PropertyT (State s) into a PropertyT IO, which feels like it would have the same problem with [].
So, will that do what I want? I'm not really sure how to investigate further other than "try it and hope I don't run into errors that I don't understand".
The text was updated successfully, but these errors were encountered:
I have a test pattern that looks like: when I generate objects, put them in state; then when I generate other objects that reference them, I can look them up. So if a
File
references aFolder
, and I want to create aFile
, I have two options:Folder
explicitly and pass it to theFile
generatorFolder
s in state and either pick one of them or generate a new one using some default generatorThis makes it easy to say "give me three different
File
s, which may or may not be in the sameFolder
".But, it seems that shrinking doesn't work like I'd hoped. Here's a simple demonstration:
I'd hope these would be basically the same: generate two numbers, save them in state, then pick one of the numbers that was saved. We have
runTateT numGen_ [] :: Gen ((Int, [Int]), [Int]))
and for every value generated, the two lists should be equal and the single value should be contained in them.This property holds for the immediately generated values, and for shrunk values from
numGen1
:But it fails for shrunk values from
numGen2
:where the two lists are different, though somehow the single value is still contained in both of them.
I think the culprit is that
Gen.list
does something complicated:it's not clear to me why it does this, but the term "interleave" makes me think it's about rearranging the shrink tree, where the default behavior would only shrink one element at a time?
(If we replace
Gen.list (Range.constant 2 2)
withreplicateM 2
, thennumGen2
behaves likenumGen1
.)So I guess I'm asking if this kind of thing is expected behavior for
StateT s Gen a
; and is there a way to do the kind of thing I'm trying to do without avoidingGen.list
entirely?I've wondered about using
GenT (State s) a
instead, but I don't know how that would work. There'shoist
to turn it into aGen a
, andhoist (Identity . flip evalState [])
typechecks. Does it work? It's not obviously wrong, I can simply change the type ofnumGen2
toGenT (State [Int]) (Int, [Int])
and do...but I've lost access to the state variables here, so I can't tell what's going on with that, and passing in
[]
feels like I might be losing state somewhere? But I don't know. Similarly I could useforAllT
, but then I'd need to turn aPropertyT (State s)
into aPropertyT IO
, which feels like it would have the same problem with[]
.So, will that do what I want? I'm not really sure how to investigate further other than "try it and hope I don't run into errors that I don't understand".
The text was updated successfully, but these errors were encountered: