Обновление внешней монады только в преобразователе монад

У меня есть монада для вычислений, которые могут завершиться ошибкой и вести журнал:

f1 :: WriterT [String] (Either String) a

У меня есть функция, которая не выйдет из строя, но ведет журнал:

f2 :: Writer [String] b

Каков наилучший способ обновить монаду записи в f1, используя журнал из f2, и зафиксировать вывод вычислений f2? На данный момент я делаю это:

f2result <- (\(r,l) -> do {tell l; return r}) (runWriter f2)

Я использую lift для обновления внутренней монады с помощью другого вычисления, поэтому переключение между монадами Writer и Someone не решит проблему.


person mskel    schedule 14.03.2012    source источник
comment
Спасибо Данр и Рэмпион за ваши ответы. Я решил использовать метод обертывания. Хотя я мог бы изменить тип f2, эта функция появляется в других контекстах, поэтому я хотел бы написать ее тип без привязки к потребностям конкретной вызывающей функции.   -  person mskel    schedule 16.03.2012


Ответы (2)


Если вы определили f2, самым простым подходом может быть рефакторинг f2, чтобы он был определен следующим образом:

 f2 :: Monad m => WriterT [String] m b

Что не должно быть слишком сложно, поскольку Writer w b определяется как WriterT w Identity b, а монада Identity ничего вам не дает.

Тогда вы сможете связать их в цепочку, просто выполнив f1 >> f2.

Если вы не можете переопределить f2, вы всегда можете определить свой собственный с соответствующей подписью:

 f2' :: Monad m => WriterT [String] m b
 f2' = WriterT . return $ runWriter f2

И если у вас есть куча f2 для переноса, вы всегда можете определить функцию для их переноса.

 wrap :: Monad m => Writer w b -> WriterT w m b
 wrap = WriterT . return . runWriter

Так что вы можете сделать f1 >> wrap f2a >> wrap f2b >> wrap f2c ...

person rampion    schedule 14.03.2012

В качестве дополнения к ответу Рэмпиона вы можете вместо этого реорганизовать f2 на любом MonadWriter:

f2 :: MonadWriter [String] m => m a

Если невозможно изменить его определение, вы можете обернуть его так же, как это делает Рэмпион:

f2' :: MonadWriter [String] m => m a
f2' = do let (a,w) = runWriter f2
         tell w
         return a

Аргумент [String] для MonadWriter требует этой прагмы GHC:

{-# LANGUAGE FlexibleContexts #-}

Как всегда, прагмы размещаются в верхней части модуля.

В комментариях рампион дал вариант обёртывания функции в такой настройке:

wrap :: MonadWriter w m => Writer w b -> m b
wrap = uncurry (<<) . (return *** tell) . runWriter 
  where (<<) = flip (>>)
person danr    schedule 14.03.2012
comment
и оболочка становится wrap :: MonadWriter w m => Writer w b -> m b; wrap = uncurry (<<) . (return *** tell) . runWriter where (<<) = flip (>>) - person rampion; 14.03.2012
comment
Что ж, позвольте мне присоединиться к обществу взаимного восхищения и сказать, что я могу оценить обобщение на класс типов MonadWriter :) - person rampion; 14.03.2012