Добавление ограничения 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)
Другие вопросы по тегам