Ich schreibe ein Programm, das als Daemon läuft. den Daemon zu erstellen, liefert der Benutzer eine Reihe von Implementierungen für jede der erforderlichen Klassen (einer von ihnen ist eine Datenbank) Alle diese Klassen haben Funktionen Typsignaturen der Form StateT s IO a
, aber s
anders haben, ist für jede Klasse.Kombinieren mehrerer Zustände in StateT
Angenommen, jede der Klassen dieses Muster folgt:
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.
Jetzt kann ich definieren einen Datensatz, der die Umsetzung von der Benutzer für jeden „Schlitz“ gewählt darstellt.
data MultiTool h = MultiTool {
hammer :: h
-- Plus additional fields for wrenches, screwdrivers, etc.
}
Und der Dämon hat den größten Teil seiner Arbeit in der StateT (MultiTool h ...) IO()
Monade.
Jetzt, da das Multitool einen Hammer enthält, kann ich es in jeder Situation verwenden, wo ein Hammer benötigt wird. Mit anderen Worten, die MultiTool
Typ können jede der Klassen implementieren es enthält, wenn ich Code wie folgt schreiben:
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
Aber die Implementierungen von withHammer
, withWrench
, withScrewdriver
usw. sind im Grunde identisch. Es wäre schön, um etwas so schreiben ...
--withMember accessor runProgram = do
-- u <- get
-- stateMap (\h -> u {accessor=h}) accessor runProgram
-- instance Hammer h => Hammer (MultiTool h) where
-- driveNail = withMember hammer driveNail
Aber das ist natürlich nicht kompiliert werden.
Ich vermute, dass meine Lösung zu objektorientiert ist. Gibt es einen besseren Weg? Monade Transformatoren, vielleicht? Vielen Dank im Voraus für Anregungen.
, machte ich schnell bearbeiten, weil in der Vereinfachung, um Ihren Code die Implementierung von 'ClawHammer' Weglassen du hast etwas produziert, das wahrscheinlich nicht das war, was du meintest. –