Стандарт Control.Monad.Writer.censor
является двойным стандарту
Control.Monad.Reader.local
, при censor
изменении состояния записи
после вычисления и local
изменения состояния считывателя
перед вычислением:
censor :: (w -> w) -> Writer w a -> Writer w a
local :: (r -> r) -> Reader r a -> Reader r a
Однако монады Reader
и Writer
не полностью
симметричны. А именно, вычисление писателя дает результат в
дополнение к писательскому состоянию, и я пытаюсь написать альтернативу
версии censor
, которая использует эту асимметрию. я хочу
написать функцию
censorWithResult :: (a -> w -> w) -> Writer w a -> Writer w a
который принимает трансформатор типа a -> w -> w
, который получает
результат вычисления в дополнение к состоянию записи. Я не
см., как записать эту функцию, используя tell
, listen
и pass
.
Точное поведение, которое я ожидаю от censorWithResult
, состоит в том, что если
ma :: Writer w a
f :: a -> w -> w
и
runWriter ma = (r , y)
затем
runWriter (censorWithResult f ma) = (r , f r y)
тогда
runWriter (censor g ma) = (r , g y)
когда g :: w -> w
.
Пример
Это не обязательно для понимания вопроса, но здесь упрощенная версия мотивирующего примера:
import Control.Applicative
import Control.Monad.Writer
-- Call-trace data type for functions from 'Int' to 'Int'.
--
-- A 'Call x subs r' is for a call with argument 'x', sub calls
-- 'subs', and result 'r'.
data Trace = Call Int Forest Int
type Forest = [Trace]
-- A writer monad for capturing call traces.
type M a = Writer Forest a
-- Recursive traced negation function.
--
-- E.g. we expect that
--
-- runWriter (t 2) = (-2 , Call 2 [Call 1 [Call 0 [] 0] -1] -2)
t , n :: Int -> M Int
t x = trace n x
n x = if x <= 0 then pure 0 else subtract 1 <$> t (x - 1)
trace :: (Int -> M Int) -> (Int -> M Int)
trace h x = do
censorWithResult (\r subs -> [Call x subs r]) (h x)
-- The idea is that if 'ma :: Writer w a' and 'runWriter ma = (r , y)'
-- then 'runWriter (censorWithResult f ma) = (r , f r y)'. I.e.,
-- 'censorWithResult' is like 'Control.Monad.Writer.censor', except it
-- has access to the result of the 'Writer' computation, in addition
-- to the written data.
censorWithResult :: (a -> w -> w) -> Writer w a -> Writer w a
censorWithResult = undefined