Вопрос о применимости морфизмов монад

#haskell #monads

#хаскелл #монады

Вопрос:

Я пытаюсь что-то сделать, и я думаю, что морфизмы монад — это ответ, но я не уверен, как правильно применить их здесь. Вкратце, у меня есть следующая настройка:

 import Control.Monad.Identity
import Control.Monad.Writer

data F = F Bool Bool

instance Monoid F where
    mempty =  F True False
    (F s0 c0) `mappend` (F s1 c1) = F (s0 amp;amp; s1)
                                    (c0 || c1 || not (s0 || s1))
type S = Writer F
type SInt = S Int
  

Выше я определяю тип данных F с двумя логическими флагами, состояние которых я отслеживаю с помощью Writer monad. Например, я буду использовать их в функции foo:

 foo :: SInt -> SInt -> SInt
foo = liftM2 ( )
  

Теперь я хотел бы в определенных ситуациях реализовать другую логику обработки флагов и использовать ее foo без изменения foo сигнатуры типа. В частности, я хотел бы изменить приведенное выше определение моноида на это:

 instance Monoid F where
    mempty =  F True False
    (F s0 c0) `mappend` (F s1 c1) = F (s0 amp;amp; s1) (c0 || c1)
  

Это то, что я мог бы сделать с помощью морфизмов монад? Как?

Комментарии:

1. Возможно, просто укажите foo более общий тип, например Monoid w => Writer w Int -> Writer w Int -> Writer w Int — в этом случае вы можете создавать экземпляры foo с разными моноидами.

Ответ №1:

Если вы действительно хотите повторно foo использовать, вы можете просто сделать его полиморфным…

 foo :: (Monoid b) => Writer b Int -> Writer b Int -> Writer b Int
  

… и предоставьте ваши экземпляры двум разным типам (я буду записывать их как newtypes кортежи, но вы можете сделать это по-другому, если хотите):

 newtype F = F { unF :: (Bool, Bool) }
    deriving (Eq, Show)

instance Monoid F where -- etc.

newtype G = G { unG :: (Bool, Bool) }
    deriving (Eq, Show)

instance Monoid G where -- etc.
  

Что касается использования монадного морфизма, вы, безусловно, можете написать такую функцию, как…

 bar :: Writer F a -> Writer G a
bar = writer . fmap (G . unF) . runWriter
  

… который эффективно просто меняет Monoid экземпляр, как вы изначально хотели. Однако вы не сможете использовать механизм mmorph, поскольку на самом деле это не морфизм монады. Цитирование Control.Monad.Morph :

 -- A monad morphism is a natural transformation:

 morph :: forall a . m a -> n a

-- ... that obeys the following two laws:

 morph (m >>= f)  = morph m >>= morph . f
 morph (return x) = return x
  

Очевидная реализация bar нарушает первый из этих законов, поскольку mappend s неявно присутствует в m >>= f и morph m >>= morph . f может давать разные результаты, даже если базовые пары логических значений одинаковы.

Комментарии:

1.@krokodil Тип RHS в первом законе равен n a (функция справа от (>>=) f тогда morph ), и при сравнении используется полное n a монадическое значение. (Кстати: стоит подчеркнуть, что, пока вы не планируете использовать bar with hoist embed и другие инструменты Control.Monad.Morph , вам не нужно беспокоиться о том, что это не морфизм монады.)