我正在编写一个作为守护进程运行的程序。为了创建守护程序,用户为每个所需的类(其中一个是数据库)提供一组实现。所有这些类都具有具有形式类型签名的函数StateT s IO a
,但s
每个类都不同。
假设每个类都遵循这种模式:
import Control.Monad (liftM)
import Control.Monad.State (StateT(..), get)
class Hammer h where
driveNail :: StateT h IO ()
data ClawHammer = MkClawHammer Int -- the real implementation is more complex
instance Hammer ClawHammer where
driveNail = return () -- the real implementation is more complex
-- Plus additional classes for wrenches, screwdrivers, etc.
现在我可以定义一个记录,表示用户为每个“插槽”选择的实现。
data MultiTool h = MultiTool {
hammer :: h
-- Plus additional fields for wrenches, screwdrivers, etc.
}
StateT (MultiTool h ...) IO ()
守护进程在monad中完成大部分工作。
现在,由于多功能工具包含锤子,我可以在需要锤子的任何情况下使用它。换句话说,MultiTool
如果我编写这样的代码,该类型可以实现它包含的任何类:
stateMap :: Monad m => (s -> t) -> (t -> s) -> StateT s m a -> StateT t m a
stateMap f g (StateT h) = StateT $ liftM (fmap f) . h . g
withHammer :: StateT h IO () -> StateT (MultiTool h) IO ()
withHammer runProgram = do
t <- get
stateMap (\h -> t {hammer=h}) hammer runProgram
instance Hammer h => Hammer (MultiTool h) where
driveNail = withHammer driveNail
但 , , 等的实现withHammer
基本withWrench
相同withScrewdriver
。能写出这样的东西就好了……
--withMember accessor runProgram = do
-- u <- get
-- stateMap (\h -> u {accessor=h}) accessor runProgram
-- instance Hammer h => Hammer (MultiTool h) where
-- driveNail = withMember hammer driveNail
但这当然不会编译。
我怀疑我的解决方案过于面向对象。有没有更好的办法?单子变压器,也许?提前感谢您的任何建议。