#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
withhoist
embed
и другие инструментыControl.Monad.Morph
, вам не нужно беспокоиться о том, что это не морфизм монады.)