0
votes

I'm trying to do a wrapper newtype for my Ap ZipList based on the advice given at Haskell quickBatch testing: Applicative Monoid ZipList. My version of GHC is 8.8.4 and I compile using stack. My full code:

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

The basic quickBatch $ monoid app tests run well without problems.

The problems I face are that when I run quickBatch $ monoid monapp, the monoid tests hangs at the mconcat test and could not be stopped. I had to close my WSL Bash in order to stop it from running.

When I tried quickBatch $ functor monapp or quickBatch $ applicative monapp, errors were thrown, thereby making the effectiveness of the wrapper newtype questionable: Couldn't match type ‘Sum Int’ with ‘(a0, b0, c0)’ Expected type: MonZipList (a0, b0, c0) Actual type: MonZipList (Sum Int) In the first argument of ‘functor’, namely ‘monapp’

Are there any solutions to these newtype wrapper problems?

1

1 Answers

1
votes

It looks like you're running into two separate problems, neither of which is really the result of using a newtype wrapper.

First, ZipList has infinite lists under the hood in the case of mempty, which is probably why your monoid monapp test is hanging. It looks like you're taking care of this in your EqProp (Ap ZipList a) instance, but not in your EqProp (MonZipList a) instance. That's a little strange. Perhaps what you meant to do was to remove the EqProp (Ap ZipList a) instance altogether and just have the EqProp (MonZipList a) instance, but use the code from the EqProp (Ap ZipList a) instance? Something like:

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

As for functor monapp, the types don't line up at all. For functor and applicative, you need to provide 3 arbitrary types for the tests to use.

You may not know this, but the actual value you provide to, e.g., functor is irrelevant -- it's only interested in the types. Personally, I find it easier to just provide the types as arguments for these functions directly, as in:

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

Now it becomes clear what's going on: for the functor tests, use the MonZipList functor along with arbitrary (and coarbitrary) values from the types Int, Bool, and Char for testing. You can feel free to change those types or to run multiple functor tests if you really want to test it with other types too.