GHC Prelude中列表的默认monoid是连接。
[1,2,3] <> [4,5,6]
变为[1,2,3] ++ [4,5,6]
,因此[1,2,3,4,5,6]
我想编写一个ZipList Monoid实例,其行为如下:
[
1 <> 4
, 2 <> 5
, 3 <> 6
]
结果是[5,7,9]
,假设我正在使用总和幺半群。
请注意,此行为与zipWith (+)
可能会表现得像这样:
[
Sum 1 <> Sum 4
, Sum 2 <> Sum 5
, Sum 3 <> Sum 6
]
我需要围绕ZipList
newtype和Sum
newtype创建一个新类型,以便为Monoid
,Arbitrary
和EqProp
创建一个实例。
从而避免了孤儿实例。
这就是ZipList
中Sum
和Prelude
的样子:
newtype ZipList a = ZipList { getZipList :: [a] }
newtype Sum a = Sum { getSum :: a }
这就是我的newtype MyZipList
看起来的样子:它看起来不错吗?
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
这就是我的newtype MySum
的样子:
这看起来不错吗?
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
我想帮助找出我哪里出错。
答案 0 :(得分:6)
首先请注意,ZipList
的{{1}}实例已经具有您想要的zippy行为。
Applicative
然后使用任何ghci> liftA2 (<>) (Sum <$> ZipList [1,2,3]) (Sum <$> ZipList [4,5,6]) :: ZipList Int
ZipList [Sum 5, Sum 7, Sum 9]
通过幺半体仿函数本身提升其内容的幺半群行为而产生Applicative
的事实。计划是从我上面写的表达式中抽象Monoid
模式。
liftA2 (<>)
(据我所知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
newtype
遗漏base
,这似乎是对我的疏忽,虽然可能有充分的理由。事实上,我认为ZipList
应该有一个开箱即用的Monoid
实例,但是,唉,它没有。)
您想要的Monoid
只是Ap ZipList (Sum Int)
。这相当于您手动编写的MyZipList
Monoid
(mempty
中的错误除外 - 它应该是MyZipList $ ZipList $ repeat mempty
),但是可以用可复制的{{{ 1}}这样的事情不那么特别,需要较少的样板。