ZipList Monoid haskell
Моноид по умолчанию для списков в GHC Prelude - это конкатенация.
[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
новый тип и Sum
newtype для того, чтобы создать экземпляр для Monoid
, Arbitrary
, а также EqProp
, Таким образом избегая сиротских случаев. Вот как оба ZipList
и Sum
выглядит как в Prelude
:
newtype ZipList a = ZipList { getZipList :: [a] }
newtype Sum a = Sum { getSum :: a }
Вот так мой новый тип 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
Вот так мой новый тип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
Я хотел бы помочь выяснить, где я ошибся.
1 ответ
Первое замечание, что ZipList
"s 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
), но составляя его из многоразового использования newtype
Как будто это менее специализировано и требует меньше шаблонов.