问题
The default monoid for lists in the GHC Prelude is concatenation.
[1,2,3] <> [4,5,6]
becomes [1,2,3] ++ [4,5,6]
and thus [1,2,3,4,5,6]
I want to write a ZipList Monoid instance that behaves like this:
[
1 <> 4
, 2 <> 5
, 3 <> 6
]
The result is [5,7,9]
assuming I am using the sum monoid.
Note this behaves like zipWith (+)
Potentially it would behave like this:
[
Sum 1 <> Sum 4
, Sum 2 <> Sum 5
, Sum 3 <> Sum 6
]
I need to create a newtype around the ZipList
newtype and the Sum
newtype in order to create an instance for Monoid
, Arbitrary
, and EqProp
.
Thus avoiding orphan instances.
This is how both the ZipList
and the Sum
looks like in the Prelude
:
newtype ZipList a = ZipList { getZipList :: [a] }
newtype Sum a = Sum { getSum :: a }
This is how my newtype MyZipList
looks: Does it look right?
newtype MyZipList a =
MyZipList (ZipList [a])
deriving (Eq, Show)
instance Monoid a => Monoid (MyZipList a) where
mempty = MyZipList (ZipList [])
mappend (MyZipList z) (MyZipList z') =
MyZipList $ liftA2 mappend z z'
instance Arbitrary a => Arbitrary (MyZipList a) where
arbitrary = MyZipList <$> arbitrary
instance Eq a => EqProp (MyZipList a) where
(=-=) = eq
This is how my newtypeMySum
looks like:
Does this look right?
newtype MySum a =
MySum (Sum a)
deriving (Eq, Show)
instance (Num a, Monoid a) => Monoid (MySum a) where
mempty = MySum mempty
mappend (MySum s) (MySum s') = MySum $ s <> s'
instance Arbitrary a => Arbitrary (MySum a) where
arbitrary = MySum <$> arbitrary
I would like assistance in finding out where I went wrong.
回答1:
First note that ZipList
’s Applicative
instance already has the zippy behaviour you want.
ghci> liftA2 (<>) (Sum <$> ZipList [1,2,3]) (Sum <$> ZipList [4,5,6]) :: ZipList Int
ZipList [Sum 5, Sum 7, Sum 9]
Then use the fact that any Applicative
gives rise to a Monoid
by lifting the monoidal behaviour of its contents through the monoidal functor itself. The plan is to abstract the liftA2 (<>)
pattern from the expression I wrote above.
newtype Ap f a = Ap { getAp :: f a }
instance (Applicative f, Monoid a) => Monoid (Ap f a) where
mempty = Ap $ pure mempty
Ap xs `mappend` Ap ys = Ap $ liftA2 mappend xs ys
(As far as I know this newtype
is missing from base
, which seems like an oversight to me, though there may be a good reason for it. In fact, I’d argue that ZipList
should have a zippy Monoid
instance out of the box, but, alas, it doesn’t.)
Your desired Monoid
is then just Ap ZipList (Sum Int)
. This is equivalent to the MyZipList
Monoid
you wrote by hand (except for the mistake in your mempty
- it should be MyZipList $ ZipList $ repeat mempty
), but composing it out of reusable newtype
s like this is less ad-hoc and requires less boilerplate.
来源:https://stackoverflow.com/questions/50130388/ziplist-monoid-haskell