问题描述
GHC前奏中列表的默认monoid是串联.
The default monoid for lists in the GHC Prelude is concatenation.
[1,2,3]<>[4,5,6]
变为 [1,2,3] ++ [4,5,6]
,因此成为 [1,2,3,4,5,6]
我想编写一个ZipList Monoid实例,其行为如下:
I want to write a ZipList Monoid instance that behaves like this:
[
1 <> 4
, 2 <> 5
, 3 <> 6
]
假设我使用的是sum monoid,结果为 [5,7,9]
.请注意,这的行为类似于 zipWith(+)
The result is [5,7,9]
assuming I am using the sum monoid.Note this behaves like zipWith (+)
可能它的行为如下:
[
Sum 1 <> Sum 4
, Sum 2 <> Sum 5
, Sum 3 <> Sum 6
]
我需要围绕 ZipList
新类型和 Sum
新类型创建一个新类型,以便为 Monoid
,创建实例任意
和 EqProp
.从而避免了孤立实例.这是 ZipList
和 Sum
在 Prelude
中的样子:
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 }
这是我的新类型 MyZipList
的外观:看起来正确吗?
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
这是我的新类型 MySum
的外观:这看起来正确吗?
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.
推荐答案
首先请注意, ZipList
的 Applicative
实例已经具有所需的zippy行为.
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]
然后利用以下事实:任何 Applicative
都会通过单调函子自身提升其内容的单调行为,从而产生 Monoid
.计划是从我上面编写的表达式中提取 liftA2(<>)
模式.
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
(据我所知, base
中缺少此 newtype
,这对我来说似乎是一个疏忽,尽管可能有充分的理由.事实上,我认为 ZipList
应该开箱即用的zippy Monoid
实例,但是,可惜没有.)
(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.)
您所需的 Monoid
就是 Ap ZipList(Sum Int)
.这等效于您手工编写的 MyZipList
Monoid
(除了 mempty
中的错误-它应该是 MyZipList $ ZipList$ repeat mempty
),但是像这样用可重用的 newtype
组成它是临时性的,并且需要更少的样板.
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.
这篇关于ZipList Monoid haskell的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!