0

I'm trying to do a quickBatch test on the solution provided at ZipList Monoid haskell. I'll need some advice as to how to continue from here or should I try something else for EqProp (Ap f a)? How do I go about deriving a solution for this?

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 f a) where
  xs =-= ys = xs' `eq` ys' where 
    xs' = 
      let (Ap l) = xs
        in take 3000 l
    ys' = 
      let (Ap l) = ys
        in take 3000 l
main :: IO ()
main = do
  quickBatch $ monoid app

There are 2 similar error messages for this code, each for the 2 lines: in take 3000 l

Error message:

Couldn't match type ‘f’ with ‘[]’
‘f’ is a rigid type variable bound by
the instance declaration at Line of Code
Expected type: [a]
Actual type: f a
In the second argument of ‘take’, namely ‘l’
In the expression: take 3000 l
maxloo
  • 453
  • 2
  • 12
  • Just... don't call `take`? Why is that in there anyway? (In fact you can probably shorten it even further to just `(=-=) = eq`.) – Daniel Wagner Jan 16 '21 at 17:35
  • 1
    Please delete the image of your error message and instead copy and paste the actual text of the message. Images of text are inaccessible to visually impaired users and useless for searching. – dfeuer Jan 16 '21 at 18:04
  • 1
    @DanielWagner, the `take` is presumably to avoid infinite loops checking, e.g., `mempty <> mempty = mempty`. – dfeuer Jan 16 '21 at 18:09
  • @dfeuer Aaaah, yeah, that's... unfortunate. – Daniel Wagner Jan 16 '21 at 18:47
  • @DanielWagner, I guess it's actually *just* for `mempty <> mempty = mempty`. So the cutoff could be limited to pairs of lists whose first `n` elements are all `mempty`. But that starts to get a bit silly. – dfeuer Jan 16 '21 at 19:07

1 Answers1

2

The problem is that you say

instance Eq a => EqProp (Ap f a)

but then in the instance you use take, which works only for lists and not arbitrary type constructors f. For testing purposes, it would be reasonable to just limit the instance with either

instance Eq a => EqProp (Ap ZipList a)

or

instance (Eq a, f ~ ZipList) => EqProp (Ap f a)

You'll still need to unwrap the ZipLists. Either of these will require additional language extensions; just do what the error messages say. Outside of testing, it's usually better practice to use a newtype wrapper: something like

{-# language GeneralizedNewtypeDeriving, DeriveTraversable #-}
newtype MonZipList a = MonZipList (Ap ZipList a)
  deriving (Applicative, Alternative, Semigroup
    , Monoid, Functor, Foldable, Traversable, ...)
dfeuer
  • 48,079
  • 5
  • 63
  • 167
  • Thanks, I've tried: `instance Eq a => EqProp (Ap ZipList a) where ..`, but I got another error message at the same lines of code: `Couldn't match expected type ‘[a1]’ with actual type ‘ZipList a’ In the second argument of ‘take’, namely ‘l’ In the expression: take 3000 l`. – maxloo Jan 16 '21 at 18:58
  • @maxloo, right, you have to unwrap the `ZipList`, not just the `Ap`. – dfeuer Jan 16 '21 at 19:00
  • @maxloo, `getZipList . getAp` should do the trick. – dfeuer Jan 16 '21 at 19:01
  • Thanks, that works! Could I also find out how MonZipList is used? – maxloo Jan 16 '21 at 19:27
  • @maxloo huh? That's rather vague. What do you still not understand? – dfeuer Jan 16 '21 at 19:29
  • I do not quite understand how MonZipList is used. Does that mean I should declare instead something like: `newtype Ap f a = Ap { getAp :: f a } deriving (Eq, Show, Semigroup, Monoid, Functor, Applicative)`? – maxloo Jan 16 '21 at 19:55
  • @maxloo, the idea is that instead of using `Ap ZipList` directly for tests, you can write a wrapper newtype carrying that, give that wrapper an `EqProp` instance, and derive everything else you need. It's probably overkill in practice for a test suite, but it avoids the potential problems of having an orphan instance. – dfeuer Jan 16 '21 at 20:08
  • Ok, I've tried: `newtype MonZipList a = MonZipList (Ap ZipList a) deriving (Applicative, Semigroup, Monoid, Functor, Eq, Show)`, but got 2 similar errors for Applicative and Functor: `No instance for (Applicative (Ap ZipList)) arising from the 'deriving' clause of a data type declaration. Possible fix: use a standalone 'deriving instance' declaration, so you can specify the instance context yourself.` But when I tried `deriving instance (Applicative, Semigroup, Monoid, Functor, Eq, Show)`, I get the error `parse error on input ‘instance’`. So, what should I do? – maxloo Jan 16 '21 at 20:40
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/227424/discussion-between-maxloo-and-dfeuer). – maxloo Jan 16 '21 at 20:50
  • I've posted my reply in the chat, hope to hear from you.. – maxloo Jan 17 '21 at 11:07