我不認爲SbT
的定義是你想要的。這定義了函數組合,並且假設m
參數是Functor
或Applicative
,這應該保留這些屬性。但是這樣的構成通常不會從另外兩個中創造一個新的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
包裝。這兩種方式都使它成爲一個單一的變壓器,隱藏了它實際上是一個複合材料的事實。
如果願意,通過提取組合變換器的實例,編寫MonadReader
和MonadState
的實例應該很簡單。
這將做到這一點。謝謝! – dsign