Обновление внешней монады только в монадном трансформаторе - PullRequest
7 голосов
/ 14 марта 2012

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

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

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

f2 :: Writer [String] b

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

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

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

Ответы [ 2 ]

4 голосов
/ 14 марта 2012

В качестве ответа на ответ Rampion, вы можете вместо этого рефакторинг f2 на любой MonadWriter:

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

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

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

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

{-# LANGUAGE FlexibleContexts #-}

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

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

wrap :: MonadWriter w m => Writer w b -> m b
wrap = uncurry (<<) . (return *** tell) . runWriter 
  where (<<) = flip (>>)
4 голосов
/ 14 марта 2012

Если вы определили 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 ...

...