使用 QuickCheck 将 ST 与多态测试“模板”结合使用

问题描述

我有一个具有关联 monad 的类,因此可以为不同的 monad 创建实例。

class MutRef a where
  type MutRefM a :: * -> *
  type MutRefVal a

  readMutRef :: a -> MutRefM a (MutRefVal a)
  writeMutRef :: a -> MutRefVal a -> MutRefM a ()
  modifyMutRef :: a -> (MutRefVal a -> MutRefVal a) -> MutRefM a ()

我想用 QuickCheck 测试这些。

我想创建代表“法律”的函数,如下所示:

readsAndWrites :: (MutRef a,Monad (MutRefM a),Eq (MutRefVal a))
    => TestHarness a -> MutRefVal a -> Property 
readsAndWrites (getRef,runner) a = runner $ do
  a' <- run $ do
    ref <- getRef
    writeMutRef ref a
    readMutRef ref
  assert (a == a')

TestHarness 是辅助函数元组

type TestHarness a = (MutRefM a a,PropertyM (MutRefM a) () -> Property)

为测试提供要使用的“Ref”,并将操作分别转换为 Property

然后,我将能够使用“法律”和工具来实例化 QuickCheck 测试属性

对于 IO,这没有问题:

ioRefTestHarness :: TestHarness (IORef Int)
ioRefTestHarness = (newIORef 0,monadicIO)

prop_ioRefReadsAndWrites :: MutRefVal (IORef Int) -> Property
prop_ioRefReadsAndWrites = readsAndWrites ioRefTestHarness

编译并运行。

不过,我也有一个 ST 实例:

instance MutRef (STRef s a) where
  type MutRefM (STRef s a) = ST s
  type MutRefVal (STRef s a) = a

  readMutRef = readSTRef
  writeMutRef = writeSTRef
  modifyMutRef = modifySTRef

然而,尝试天真地定义测试工具,

stRefTestHarness :: TestHarness (STRef s Int)
stRefTestHarness = (newSTRef 0,monadicST)

失败并显示错误消息:

Couldn't match type ‘PropertyM (ST s) ()’
                     with ‘forall s1. PropertyM (ST s1) a0’
      Expected type: PropertyM (MutRefM (STRef s Int)) () -> Property
        Actual type: (forall s. PropertyM (ST s) a0) -> Property
    • In the expression: monadicST
      In the expression: (newSTRef 0,monadicST)
      In an equation for ‘stRefTestHarness’:
          stRefTestHarness = (newSTRef 0,monadicST)
    • Relevant bindings include
        stRefTestHarness :: TestHarness (STRef s Int)
          (bound at test/quickcheck.hs:79:1)

我猜我需要在某个地方进行量化,但我一直没有通过反复试验成功地找出哪里。

是否可以像这样通过 ST 参数化我的线束类型?

我该如何去做我想做的事?

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)