2014-01-30 23 views
11

標準Control.Monad.Writer.censor是雙重的標準 Control.Monad.Reader.local,與censor修改寫入器狀態 計算和local後修改所述讀取器狀態 之前計算:如何編寫這個更通用的`Control.Monad.Writer.censor`版本?

censor :: (w -> w) -> Writer w a -> Writer w a 
local :: (r -> r) -> Reader r a -> Reader r a 

然而,ReaderWriter monads並非完全對稱的 。也就是說,一個寫作者計算產生了結果, 除了寫作者狀態,並且我試圖編寫版本的censor利用這種不對稱性。我想 編寫一個函數

censorWithResult :: (a -> w -> w) -> Writer w a -> Writer w a 

這需要,除了向筆者狀態下接收計算的 結果a -> w -> w類型的變壓器。我不用 瞭解如何使用telllistenpass來編寫此功能。

精確的行爲我期待的​​是,如果

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 

回答

5

如果我們只允許使用tellpasslisten,即能訪問輸出的唯一功能是

-- | `pass m` is an action that executes the action `m`, which returns a value 
-- and a function, and returns the value, applying the function to the output. 
pass :: (MonadWriter w m) => m (a, w -> w) -> m a 

所以對於​​,我們需要a -> w -> w類型的給定函數部分適用於得到w -> w並將其處理爲pass。這可以作爲

censorWithResult :: (MonadWriter w m) => (a -> w -> w) -> m a -> m a 
censorWithResult f m = pass $ do 
    a <- m 
    return (a, f a) 

內部pass動作執行給定動作來完成的,部分適用f它和pass然後相應地修改輸出。

21

精確的行爲我期待的​​是,如果

ma :: Writer w a 
f :: a -> w -> w 

and

runWriter ma = (r , y) 

然後

runWriter (censorWithResult f ma) = (r , f r y) 

好吧,讓我們這樣做,那麼。唯一需要知道的是writerrunWriter的左反。然後我們得到下面的等式鏈,首先通過向雙方應用writer,然後通過消除左反。

 runWriter (censorWithResult f ma) =  (r, f r y) 
writer (runWriter (censorWithResult f ma)) = writer (r, f r y) 
        censorWithResult f ma = writer (r, f r y) 

,我們現在需要做的唯一的事情就是插入您的公式runWriter ma = (r, y)

censorWithResult f ma = let (r, y) = runWriter ma in writer (r, f r y) 

是不是等式推理盛大?

+0

在monad外面思考的方法:D – ntc2