2011-09-14 64 views
5

對不起,如果這個問題似乎有點微不足道......它不適合我。 我愉快地由下列單子:作爲變壓器摘要monad作文

type SB i a = ReaderT (AlgRO i) (State (AlgState i)) a 

其是,阱,表現良好的單子。 ReaderT是一個monad變換器,State是狀態monad,AlgRO和AlgState是分別在i中爲mutable和read-only狀態分別參數化的數據類型。現在,如果我想製作一個整潔的單色變壓器與新類型,像這樣:

newtype SbT m i a = SbT { 
    runSbT:: m (SB i a) 
} 

我應該如何繼續?我甚至無法設法將Monad類型的綁定方法放在一起,更不用說MonadTrans的「提升」了......我想這種自動推導可能有所幫助,但我想知道它在這種情況下的工作原理。

在此先感謝。

回答

10

我不認爲SbT的定義是你想要的。這定義了函數組合,並且假設m參數是FunctorApplicative,這應該保留這些屬性。但是這樣的構成通常不會從另外兩個中創造一個新的monad。有關該主題的更多信息,請參閱this question

那麼,如何你創建你想要的monad變壓器呢?雖然monad不直接構成,monad 變壓器可以組成。因此,要從現有的變壓器中構建一個新的變壓器,您基本上只想爲該構成命名。這與您所用的newtype不同,因爲您直接應用m,而不是將它傳遞到變壓器堆棧。

有關定義monad變壓器的一點需要注意的是,它們必然以某種方式「向後」工作 - 當您將複合變壓器應用於monad時,「最內層」變壓器會獲得第一個裂縫,它產生的轉換後的monad是下一個變壓器的工作,& c。請注意,這與將一個組合函數應用於參數時得到的順序沒有任何區別,例如, (f . g . h) x首先給出h的參數,即使f是組合中的「第一個」功能。

好了,你的組合變壓器需要考慮它的應用到單子,並把它傳遞給最裏面的變壓器,這是,嗯....哎呀,原來,SB已經應用於單子。難怪這不起作用。首先,我們需要刪除它。它在哪裏?不是State - 我們可能刪除,但我們不想,因爲它是你想要的一部分。嗯,但等一下 - State又是什麼意思?哦耶:

type State s = StateT s Identity 

啊哈,我們走了。讓我們從那裏得到Identity

type SB i a = ReaderT (AlgRO i) (State (AlgState i)) a 

等效形式:

type SB i a = ReaderT (AlgRO i) (StateT (AlgState i) Identity) a 

然後我們踢懶燒盡:

type SB' i m a = ReaderT (AlgRO i) (StateT (AlgState i) m) a 
type SB i a = SB' i Identity a 

但現在SB'看起來很像一個單子轉換我們從您目前的定義去定義,並有充分的理由,因爲它是。因此,我們重新創建newtype包裝,折騰了幾個例子在那裏:

newtype SbT i m a = SbT { getSB :: ReaderT (AlgRO i) (StateT (AlgState i) m) a } 

instance (Functor m) => Functor (SbT i m) where 
    fmap f (SbT sb) = SbT (fmap f sb) 

instance (Monad m) => Monad (SbT i m) where 
    return x = SbT (return x) 
    SbT m >>= k = SbT (m >>= (getSB . k)) 

instance MonadTrans (SbT i) where 
    lift = SbT . lift . lift 

runSbT :: SbT i m a -> AlgRO i -> AlgState i -> m (a, AlgState t) 
runSbT (SbT m) e s = runStateT (runReaderT m e) s 

幾件事情要注意的:這裏的runSbT功能不是字段訪問,而是爲每一個組成的「運行」功能我們知道堆棧中的變壓器。同樣,lift函數必須爲兩個內部變壓器提升一次,然後添加最終的newtype包裝。這兩種方式都使它成爲一個單一的變壓器,隱藏了它實際上是一個複合材料的事實。

如果願意,通過提取組合變換器的實例,編寫MonadReaderMonadState的實例應該很簡單。

+0

這將做到這一點。謝謝! – dsign

2

你打算在新類型的東西周圍包裝一個額外的m嗎?我會建議如下:

newtype Sb i a = Sb { runSb :: SB i a } 

...這應該使你的instance Monad (Sb i)有點容易寫。如果你真的想寫一個monad變壓器,那麼你應該一直使用變壓器;例如,

type SBT m i a = ReaderT (AlgRO i) (StateT (AlgState i) m) a 
newtype SbT m i a = SbT { runSbT :: SBT m i a } 

作爲關注的第二點,這是通常優選的η-減少type同義詞(因爲它們必須始終是「完全應用」);與SB和做這個看起來是這樣的:

type SB i = ReaderT (AlgRO i) (State (AlgState i)) 
type SBT m i = ReaderT (AlgRO i) (StateT (AlgState i) m) 
+0

順便說一下,你不能用這種方式使'SbT'變成單子變壓器。變形金剛具有種類'(* - > *) - > * - > *',即將monad和類型作爲參數。 'i'參數必須是第一個,所以你可以部分應用它。 –