Я пишу программу, которая работает как демон.
Для создания демона пользователь предоставляет набор
реализации для каждого из необходимых классов (одна из них - база данных)
Все эти классы имеют функции
типа подписи формы 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
Но, конечно, это не скомпилируется.
Я подозреваю, что мое решение слишком объектно-ориентированное. Есть ли способ лучше? Модадские трансформаторы, может быть? Заранее благодарю вас за любые предложения.