【发布时间】:2012-12-04 15:40:15
【问题描述】:
我正在编写一个作为守护进程运行的程序。
要创建守护进程,用户需要提供一组
每个所需类的实现(其中一个是数据库)
所有这些类都有功能
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 () 中完成大部分工作
单子。
现在,由于多功能工具包含一把锤子,我可以在任何情况下使用它
需要锤子的地方。换句话说,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
但这当然不会编译。
我怀疑我的解决方案过于面向对象。 有没有更好的办法? 单子变压器,也许? 提前感谢您的任何建议。
【问题讨论】:
-
顺便说一句,我对您的代码进行了快速编辑,因为在您的简化中省略了
ClawHammer的实现,您产生的东西可能不是您的意思。