ZipList Monoid haskell

隐身守侯 提交于 2021-02-04 07:29:08

问题


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 newtypes like this is less ad-hoc and requires less boilerplate.



来源:https://stackoverflow.com/questions/50130388/ziplist-monoid-haskell

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!