Добавление ограничения Monoid к скрытой переменной
Я в главе Книги Хаскелла о Applicative
, Я пишу Applicative
экземпляр для ZipList
и я знаю, что переосмыслил это и меняю свой подход. Мой старый подход был:
data List a = Nil | Cons a (List a) deriving (Eq, Show)
newtype ZipList' a =
ZipList' (List a)
deriving (Eq, Show)
instance Applicative ZipList' where
pure a = ZipList' (Cons a Nil)
ZipList' Nil <*> _ = ZipList' Nil
_ <*> ZipList' Nil = ZipList' Nil
(ZipList' (Cons a bs)) <*> (ZipList' (Cons a' bs')) = (ZipList' (Cons (a(a')) Nil)) `mappend` (ZipList' bs <*> ZipList' bs')
Я получаю ошибку:
No instance for (Monoid b) arising from a use of ‘mappend’
Possible fix:
add (Monoid b) to the context of
the type signature for:
(<*>) :: forall a b. ZipList' (a -> b) -> ZipList' a -> ZipList' b
• In the expression:
(ZipList' (Cons (a (a')) Nil))
`mappend` (ZipList' bs <*> ZipList' bs')
In an equation for ‘<*>’:
(ZipList' (Cons a bs)) <*> (ZipList' (Cons a' bs'))
= (ZipList' (Cons (a (a')) Nil))
`mappend` (ZipList' bs <*> ZipList' bs')
In the instance declaration for ‘Applicative ZipList'’
Я думаю, это потому, что mappend
использует Monoid
за ZipList
который:
instance Monoid a => Monoid (ZipList' a) where
mempty = pure mempty
mappend = liftA2 mappend
который ставит Monoid
ограничение на a
, в Applicative
экземпляр я не могу добавить a
к экземпляру класса без переставания быть надлежащим Applicative
определение экземпляра Я знаю, что решение неверно, но оно заставило меня задуматься: "Как бы я добавил Monoid
ограничение на аргумент ZipList
в Applicative
пример?
2 ответа
Здесь не нужно ничего добавлять. Не создавайте список Cons x Nil
и добавить список y
Построй Cons x y
,
let ZipList bs'' = ZipList' bs <*> ZipList' bs'
in ZipList' (Cons (a a') bs'')
О моноидной ошибке. Это зависит от того, какой экземпляр вы используете для вашего типа ZipList'
,
Если вы используете что-то вроде
instance Monoid (ZipList' a) where
mempty = ZipList' Nil
mappend (ZipList' Nil) zs = zs
mappend (ZipList' (Cons x xs)) zs = let
ZipList' ys = mappend (Ziplist' xs) zs
in ZipList' (Cons x ys)
тогда нет необходимости Monoid a
,
Я думаю, что все зависит от того, какой моноид вы хотите. Monoid (ZipList' a)
Вы упомянули, что каждый компонент двух списков добавляется точечно, и для этого нам нужно Monoid a
, Однако в аппликативном экземпляре вам не нужно такого рода добавление, вам нужно объединить список. Это может быть достигнуто с использованием вышеупомянутого экземпляра моноида.
При этом: экземпляр, который вы разместили, работает точечно, вероятно, самый естественный для ZipList
, Ничто, однако, не мешает вам определить неэкземплярную функцию
appendZipList' :: ZipList' a -> ZipList' a -> ZipList' a
и используйте это в своей аппликационной инстанции, не полагаясь на моноиды.
В противном случае вы могли бы определить instance Monoid (List a)
для связующего приложения, и использовать это в аппликативном случае
let ZipList' bs'' = ZipList' bs <*> ZipList' bs'
in ZipList' (Cons (a a') Nil `mappend` bs'')
В итоге я отменил требование Monoid и использовал функцию в списках, которая обошла их и применила функции слева к значениям справа.
zipForMyList :: List (t -> a) -> List t -> List a
zipForMyList _ Nil = Nil
zipForMyList Nil _ = Nil
zipForMyList (Cons f fs) (Cons a as) = (Cons (f a) (zipForMyList fs as))
repeatMyList :: p -> List p
repeatMyList x = xs where xs = Cons x xs
instance Applicative ZipList' where
pure a = ZipList' (Cons a Nil)
ZipList' Nil <*> _ = ZipList' Nil
_ <*> ZipList' Nil = ZipList' Nil
(ZipList' fs) <*> (ZipList' as) = ZipList' (zipForMyList fs as)