2017-04-19 42 views
2

摘要:使用Writer monad時,我希望能夠在不失去狀態的情況下在mappend的2個不同版本之間切換。在Writer monad中交換`mappend`

我用兩個布爾標誌來跟蹤一些狀態:

data Flags = F Bool Bool 

現在我定義了兩個Monoid情況下它的方式不同,他們結合旗幟mappend

newtype XFlags = XF Flags 

instance Monoid XFlags where 
    mempty = XF (F True False) 
    (XF (F s0 c0)) `mappend` (XF (F s1 c1)) = XF (F (s0 && s1) 
    (c0 || c1 || not (s0 || s1))) 

newtype SFlags = SF Flags 

instance Monoid SFlags where 
    mempty = SF (F True False) 
    (SF (F s0 c0)) `mappend` (SF (F s1 c1)) = SF (F (s0 && s1) (c0 || c1)) 

現在我可以有2 Writer monads不同國旗處理:

type XInt = WriterT XFlags Identity Int 
type SInt = WriterT SFlags Identity Int 

現在我可以有這樣的操作:

xplus :: XInt -> XInt -> XInt 
xplus = liftM2 (+) 

splus :: SInt -> SInt -> SInt 
splus = liftM2 (+) 

我們,我想建立一個像表達式:

foo = splus (return 1) (xplus (return 2) (return 3)) 

要做到這一點,我需要能夠在兩者之間轉換,而不會丟失任何標誌和最好不用展開monad(使用runWriter)。這部分我沒有弄清楚。它看起來有點像我可以嘗試使用monad變壓器來嵌套編寫器,但我不確定它是否可以直接在這裏使用。我會欣賞一些關於實現這樣的最佳方式的指導。

+0

你爲什麼不打開monad? – luqui

+0

理想情況下,我想monad跟蹤整個計算樹。後來我可以使用不同的Monoid來建立跟蹤或做一些其他分析。隱含地解開它會打破這個(我認爲)。完全公開:上面的例子在Haskell中,但我實際上是在Coq中實現它:) – krokodil

+1

然而,最終你要解決這個問題,你需要一種方法來將來自'xplus'的'XFlags'與來自'SFlags' 'splus'。 – Cactus

回答

2

使用mapWriter可以得到合理的結果。現在

sFromX :: XInt -> SInt 
sFromX = mapWriter (\(x, XF fs) -> (x, SF fs)) 

你可以寫foo

foo :: SInt 
foo = splus (return 1) (sFromX (xplus (return 2) (return 3))) 

您可能需要相反,xFromS爲好。如果你有兩個以上的不同類羣也許這將是值得讓發燒友和寫作標誌的容器類,像這樣:

class FlagContainer a where 
    getFlags :: a -> Flags 
    makeFromFlags :: Flags -> a 

然後用它來寫一個函數將取代sFromXxFromS和 任何你需要的東西。 (雖然沒有測試過。)