首页 > 解决方案 > Haskell quickBatch 测试:Ap ZipList 的新类型包装器

问题描述

我正在尝试根据Haskell quickBatch testing: Applicative Monoid ZipListAp ZipList中给出的建议为我做一个包装新类型。我的 GHC 版本是 8.8.4,我使用堆栈进行编译。我的完整代码:

newtype Ap f a = Ap { getAp :: f a }
  deriving (Eq, Show)
instance (Applicative f, Semigroup a) =>
  Semigroup (Ap f a) where
    Ap xs <> Ap ys = 
      Ap $ liftA2 (<>) xs ys
instance (Applicative f, Monoid a) => 
  Monoid (Ap f a) where
    mempty = Ap $ pure mempty
    Ap xs `mappend` Ap ys = 
      Ap $ liftA2 mappend xs ys
app :: Ap ZipList (Sum Int)
app = Ap (ZipList [1,2 :: Sum Int])
test :: Ap ZipList (Sum Int)
test = app <> app
instance Arbitrary (f a) =>
  Arbitrary (Ap f a) where
    arbitrary = Ap <$> arbitrary  
instance Eq a => EqProp (Ap ZipList a) where
  xs =-= ys = xs' `eq` ys' where 
    xs' = 
      let (Ap (ZipList l)) = xs
        in take 3000 l
    ys' = 
      let l = (getZipList . getAp) ys
        in take 3000 l

newtype MonZipList a = 
  MonZipList (Ap ZipList a)
  deriving (Semigroup, Monoid, Eq, Show)
deriving instance Functor f => 
  Functor (Ap f)
deriving instance Applicative f => 
  Applicative (Ap f)
monapp :: MonZipList (Sum Int)
monapp = MonZipList app
instance Arbitrary a =>
  Arbitrary (MonZipList a) where
    arbitrary = MonZipList <$> arbitrary
instance Eq a => EqProp (MonZipList a) where
  (=-=) = eq

main :: IO ()
main = do 
  quickBatch $ monoid app
  quickBatch $ monoid monapp
  quickBatch $ functor monapp
  quickBatch $ applicative monapp

基本quickBatch $ monoid app测试运行良好,没有问题。

我面临的问题是,当我运行时quickBatch $ monoid monapp,monoid 测试挂在 mconcat 测试中并且无法停止。我不得不关闭我的 WSL Bash 以阻止它运行。

当我尝试quickBatch $ functor monapporquickBatch $ applicative monapp时,抛出错误,从而使包装器 newtype 的有效性受到质疑:无法将类型 'Sum Int' 与 '(a0, b0, c0)' 预期类型匹配:MonZipList (a0, b0, c0) 实际type: MonZipList (Sum Int) 在'functor'的第一个参数中,即'monapp'</p>

这些新型包装器问题有什么解决方案吗?

标签: haskell

解决方案


看起来您遇到了两个不同的问题,这两个问题都不是使用 newtype 包装器的结果。

首先,ZipList在 的情况下,引擎盖下有无限的列表mempty,这可能就是您的monoid monapp测试挂起的原因。看起来您正在您的实例中处理这个问题EqProp (Ap ZipList a),但不是在您的EqProp (MonZipList a)实例中。这有点奇怪。也许您的意思是EqProp (Ap ZipList a)完全删除实例并拥有EqProp (MonZipList a)实例,但使用实例中的代码EqProp (Ap ZipList a)?就像是:

instance Eq a => EqProp (MonZipList a) where
  MonZipList (Ap (ZipList xs)) =-= MonZipList (Ap (ZipList ys)) = 
    take 3000 xs `eq` take 3000 ys

至于functor monapp,类型根本没有排列。对于functorapplicative,您需要提供 3 种任意类型供测试使用。

您可能不知道这一点,但是您提供给的实际functor是无关紧要的——它只对类型感兴趣。就个人而言,我发现直接为这些函数提供类型作为参数更容易,如下所示:

{-# LANGUAGE TypeApplications #-}
main = do 
  quickBatch $ monoid @(MonZipList (Sum Int)) undefined
  quickBatch $ functor @MonZipList @Int @Bool @Char undefined
  quickBatch $ applicative @MonZipList @String @Int @Char undefined

现在很清楚发生了什么:对于functor测试,使用函子以及来自 types 、和MonZipList的任意(和 coarbitrary)值进行测试。如果您真的想用其他类型进行测试,您可以随意更改这些类型或运行多个测试。IntBoolCharfunctor


推荐阅读