RankNTypes pour les déclarations d'instance?
-
08-10-2019 - |
Question
Je joue autour avec RankNTypes récemment et je me demande s'il est possible de les utiliser dans les déclarations d'instance.
Voici un exemple simple en utilisant des types de données ouvertes
data (Expr a, Expr b) => Add a b = Add a b deriving(Show)
instance (Expr a, Expr b) => Expr (Add a b)
instance (Evaluation a, Evaluation b) => Evaluation (Add a b) where
eval (Add x y) = eval x + eval y
Ici, je dois les contraintes d'écriture comme (l'évaluation a, évaluation b), mais au fond, je veux juste un écrire quelque chose comme (forall a. Une évaluation). Est-ce même possible?
Cordialement, raichoo
La solution
(forall a . Evaluation a)
ne fait pas vraiment de sens. Cela voudrait dire que chaque type simple (y compris toute personne de type avenir pourrait faire) est une instance de Evaluation
En outre, dans ce cas, je pense que votre code listant les instances de Evaluation
que vous voulez est la bonne chose à faire; ne demande pas plus que vous avez réellement besoin.
Mais il y a certainement des cas où il serait agréable d'être en mesure de quantifier sur les contraintes de classe le long des lignes que vous décrivez, et il est impossible directement. Un exemple est que vous pouvez effectuer automatiquement des instances MonadPlus
de Monoid
(en utilisant un type d'emballage pour éviter tout problème de OverlappingInstances
):
newtype MonoidWrapper m a = MonoidWrapper { unMonoidWrapper :: m a }
instance Monad m => Monad (MonoidWrapper m) where ...
instance (Monad m, forall a . Monoid (m a)) => MonadPlus (MonoidWrapper m) where
mzero = MonoidWrapper mempty
mplus (MonoidWrapper a) (MonoidWrapper b) = MonoidWrapper (mappend a b)
Vous ne pouvez pas écrire, mais en utilisant GADTs ou types existentiels vous pouvez simuler, avec une certaine douleur syntaxique:
data MonoidDict a where
MonoidDict :: Monoid a => MonoidDict a
class AlwaysMonoid m where
alwaysMonoidDict :: MonoidDict (m a) -- note the implicit forall a here
instance Monad m => Monad (MonoidWrapper m)
instance (Monad m, AlwaysMonoid m) => MonadPlus (MonoidWrapper m) where
mzero = mymzero
where
-- needed to give name to 'a' for ScopedTypeVariables
mymzero :: forall a . MonoidWrapper m a
mymzero = case (alwaysMonoidDict :: MonoidDict (m a)) of
MonoidDict -> MonoidWrapper mempty
mplus = mymplus
where
mymplus :: forall a . MonoidWrapper m a
-> MonoidWrapper m a -> MonoidWrapper m a
mymplus (MonoidWrapper a) (MonoidWrapper b)
= case (alwaysMonoidDict :: MonoidDict (m a)) of
MonoidDict -> MonoidWrapper (mappend a b)