QuickCheck生成器能否传递和获取状态?有哪些可行的实现方案?
我想要创建一个QuickCheck生成器来生成类似树的数据结构。由于该结构的特殊属性,我希望生成的值与其在结构中的深度相匹配,同时还需要存储在某一处生成的标签,以便在其他位置复用。因此我希望能够向生成器中传入状态,就像State monad中使用
put和get那样。QuickCheck库中是否存在原生支持该需求的函数,还是说我需要将StateT monad与Gen monad组合使用?quickcheck-transformer是否是可行的解决方案?
解答
原生QuickCheck没有内置支持自定义读写状态的生成器
原生Genmonad本身仅提供无状态的随机生成能力,仅内置了sized这类可以传递生成大小参数的接口,无法直接像Statemonad那样通过get/put操作自定义状态(比如共享标签、记录节点深度等)。组合
StateT和Gen是最通用的解决方案
你可以直接构造StateT s Gen a类型的monad栈,其中s是你自定义的状态类型,示例如下:data CustomGenState = CustomGenState { currentDepth :: Int -- 记录当前节点深度 , existedTags :: [String] -- 存储已经生成过的标签 }你可以在这个栈上正常使用
get/put/modify等状态操作,需要生成具体值的时候,调用runStateT传入初始状态就能拿到标准的Gen类型值,完全兼容QuickCheck的其他接口。如果需要嵌套生成子节点,只需要在递归前修改状态里的深度值即可满足深度匹配的需求,生成的标签存入状态后也可以在任意位置读取复用。quickcheck-transformer是可行的替代方案
这个库的核心就是封装了常见monad转换器和Gen的组合逻辑,已经预先实现了相关的typeclass实例,不需要你手动写各种lift调用,如果你不想手动处理monad栈的适配细节,用这个库可以减少很多模板代码。如果你的需求比较简单,手动实现StateT+Gen的组合也完全没有问题,不需要引入额外依赖。
内容的提问来源于stack exchange,提问作者JeanJouX

