我目前正在學習在 Haskell 中使用 Monad-Transformers。我使用StateTand撰寫了一個簡單的 Monad-Transformer ExceptT:
type DatabaseT m = StateT DatabaseState (ExceptT DatabaseError m)
已經有一些功能可以使用,DatabaseT例如:
connectDB :: MonadIO m => DBMode -> DatabaseT m ()
isConnected :: Monad m => DatabaseT m Bool
為了更輕松地解除此類功能,我想定義一個類似于以下內容的型別類MonadIO:
class Monad m => MonadDB m where
liftDB :: Monad m' => DatabaseT m' a -> m a
現在有問題的部分是創建DatabaseT m一個實體 if MonadDB。看這個函式的實作MonadIO應該是identity函式:
instance Monad m => MonadDB (DatabaseT m) where
liftDB = id
但是,此代碼無法編譯:
Couldn't match type ‘m'’ with ‘m’
‘m'’ is a rigid type variable bound by
the type signature for:
liftDB :: forall (m' :: * -> *) a.
Monad m' =>
DatabaseT m' a -> DatabaseT m a
‘m’ is a rigid type variable bound by
the instance declaration
Expected type: DatabaseT m' a -> DatabaseT m a
Actual type: DatabaseT m a -> DatabaseT m a
In the expression: id
In an equation for ‘liftDB’: liftDB = id
In the instance declaration for ‘MonadDB (DatabaseT m)’
Relevant bindings include
liftDB :: DatabaseT m' a -> DatabaseT m a
似乎具有 Monad-Transformer 型別類的 MonadMyMonad具有liftMyMonad類似的功能,但在MyMonadMonad-Transformer 本身時不起作用。
這是使解除DatabaseT m操作更通用的有效方法還是從根本上存在缺陷?
如果是這樣,我如何DatabaseT m從DatabaseT
堆疊頂部的任何位置提升動作?
uj5u.com熱心網友回復:
您要求的最終狀態可能不是您想要的最終狀態。也就是說,這對于大多數需求來說并不是很好:
class Monad m => MonadDB m where
liftDB :: Monad m' => DatabaseT m' a -> m a
典型的解決方案是根本不撰寫這樣的實體,而是撰寫:
class Monad m => MonadDB m where
connectDB :: DBMode -> m ()
isConnected :: m Bool
這允許您:
- 在 DatabaseT 是一個組件的 monadic 背景關系中使用函式
- 使用 MonadMock 等解決方案進行測驗
- 不破壞抽象 - 完全避免
lift在消費者代碼中使用。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/316810.html
