Disons que j'ai une classe de type :
data Foo = Foo deriving (Show)
class Monad m => MonadFoo m where
getFoo :: m Foo
Puisque GHC implémente les classes de type via le passage par dictionnaire (utilisations de SPECIALIZE
nonobstant), elle transforme effectivement getFoo
en quelque chose comme ce qui suit sous le capot :
data MonadFooDict m = MonadFooDict { getFoo :: m Foo }
...et il insère un argument supplémentaire au début des appels à getFoo
qui fait tourner le dictionnaire.
Parfois, je peux vouloir choisir une instance de manière dynamique, et il peut être souhaitable de passer moi-même un dictionnaire. Je peux simuler cela moi-même en créant une instance qui transmettra le dictionnaire à ma place.
newtype DynamicMonadFoo a = DynamicMonadFoo
{ runFoo :: MonadFooDict DynamicMonadFoo -> a }
deriving ( Functor, Applicative, Monad
, MonadReader (MonadFooDict DynamicMonadFoo) )
instance MonadFoo DynamicMonadFoo where
getFoo = join $ asks _getFoo
Maintenant, étant donné une fonction avec un MonadFoo
je peux utiliser la contrainte runFoo
pour lui passer un dictionnaire de classes de types explicite :
showCurrentFoo :: MonadFoo m => m String
showCurrentFoo = do
foo <- getFoo
return ("current foo: " ++ show foo)
ghci> runFoo showCurrentFoo MonadFooDict { _getFoo = return Foo }
"current foo: Foo"
C'est vraiment cool, mais cela semble être une tâche si simple que GHC pourrait exposer une sorte de bibliothèque pour faire cela sans tout le boilerplate (et idéalement d'une manière qui fonctionnerait mieux avec les classes de type non monadiques). Étant donné que GHC dispose de certaines capacités "semblables à la réflexion", telles que Data.Typeable
Je ne pense pas que cela soit hors du domaine du possible, mais je ne suis pas sûr que cela existe réellement sous une forme ou une autre.
Existe-t-il des modules intégrés ou d'autres bibliothèques qui permettent de faire cela de manière plus automatique ?