Sto scrivendo un programma che viene eseguito come un demone. Per creare il demone, l'utente fornisce un insieme di implementazioni per ciascuna delle classi richieste (uno di loro è un database) Tutte queste classi hanno funzioni hanno tipo firme del modulo StateT s IO a
, ma s
è diverso per ogni classeCombinazione di più stati nello Stato
Supponiamo ciascuna delle classi segue questo schema:
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.
Ora può definire un record che rappresenta l'implementazione scelto dal dall'utente per ogni "slot".
data MultiTool h = MultiTool {
hammer :: h
-- Plus additional fields for wrenches, screwdrivers, etc.
}
E il demone fa la maggior parte del suo lavoro nella monade StateT (MultiTool h ...) IO()
.
Ora, dal momento che il multiutensile contiene un martello, posso usarlo in qualsiasi situazione in cui è necessario un martello. In altre parole, il MultiTool
tipo può implementare qualsiasi delle classi in esso contenuti, se scrivo codice come questo:
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
Ma le implementazioni di withHammer
, withWrench
, withScrewdriver
, ecc sono sostanzialmente identiche. Sarebbe bello essere in grado di scrivere qualcosa di simile ...
--withMember accessor runProgram = do
-- u <- get
-- stateMap (\h -> u {accessor=h}) accessor runProgram
-- instance Hammer h => Hammer (MultiTool h) where
-- driveNail = withMember hammer driveNail
Ma, naturalmente, che non verrà compilato.
Sospetto che la mia soluzione sia troppo orientata agli oggetti. C'è un modo migliore? Trasformatori Monad, forse? Grazie in anticipo per eventuali suggerimenti.
Per inciso, ho fatto una rapida modifica al codice, perché nella vostra semplificazione omettendo l'attuazione del 'ClawHammer' hai prodotto qualcosa che probabilmente non era quello che volevi dire. –