2017-06-05 3 views
3

J'ai un constructeur de type Ast, paramétré par le type d'identifiant. En utilisant les extensions DeriveFunctor, DeriveFoldable et DeriveTraversable , il est possible de créer automatiquement les instances appropriées.Dérivation d'extensions avec types multiparamètres

Maintenant, je trouve qu'il est utile d'introduire plus de paramètres de type, mais malheureusement, la méthode ci-dessus n'est pas à l'échelle . Idéalement, je voudrais être en mesure d' envelopper mon type Ast dans les types de sélection qui me permettrait de fmap à les paramètres de type appropriés. Existe-t-il un moyen d'obtenir un effet similaire sans avoir à définir les instances moi-même?

modifier:

Voici un petit exemple de ce que l'Ast originale ressemblait à:

Ast id = Ast (FuncDef id) 
    deriving (Show, Functor, Foldable, Traversable) 

FuncDef id = FuncDef id [Fparam id] (Block id) 
    deriving (Show, Functor, Foldable, Traversable) 

Block id = Block [Stmt id] 
    deriving (Show, Functor, Foldable, Traversable) 

.. 
+3

Pouvez-vous ajouter une version réduite de 'Ast' ainsi que quelques-uns des paramètres de type et typeclasses vous voulez dérivé? – Alec

+0

C'est une idée géniale. Mon instinct me dit que vous êtes au-delà du pouvoir du mécanisme de dérivation automatique, et il est temps de commencer à regarder [génériques] (https://wiki.haskell.org/Generics). – luqui

+0

@luqui C'est ma conclusion aussi.Les extensions de langage Deriving sont là pour une raison pour laquelle elles ne semblent pas assez puissantes dans ce cas. –

Répondre

1

Après tripoter toute la journée, je suis venu aux conclusions suivantes:

Le Ast présenté en la question s'est avérée ne pas être très flexible. Dans les étapes ultérieures, je veux annoter différents nœuds d'une manière qui ne peut pas être exprimée en paramétrant simplement l'original Ast.

Alors j'ai changé le Ast pour agir en tant que base pour écrire de nouveaux types:

Ast funcdef = Ast funcdef 
    deriving (Show) 

Header id fpartype = Header id (Maybe Type) [fpartype] 
    deriving (Show) 

FuncDef header block = FuncDef header block 
    deriving (Show) 

Block stmt = Block [stmt] 
    deriving (Show) 

Stmt lvalue expr funccall = 
    StmtAssign lvalue expr | 
    StmtFuncCall funccall | 
    .. 

Expr expr intConst lvalue funccall = 
    ExprIntConst intConst | 
    ExprLvalue lvalue | 
    ExprFuncCall funccall | 
    expr :+ expr | 
    expr :- expr | 
    .. 

Maintenant, je peux simplement définir une chaîne de newtypes pour chaque étape du compilateur. Le Ast au stade de la renamer peut être paramétré dans le type d'identifiant:

newtype RAst id = RAst { ast :: Ast (RFuncDef id) } 
newtype RHeader id = RHeader { header :: Header id (RFparType id) } 
newtype RFuncDef id = RFuncDef { 
    funcDef :: FuncDef (RHeader id) (RBlock id) 
} 
.. 
newtype RExpr id = RExpr { 
    expr :: Expr (RExpr id) RIntConst (RLvalue id) (RFuncCall id) 
} 

Au cours de l'étape de typage du Ast peut être paramétré par les différents types internes utilisés dans les noeuds.

Cette paramétrisation permet de construire des Asts avec Maybe enveloppé paramètres au milieu de chaque étape.

Si tout est OK, nous pouvons utiliser fmap pour supprimer les Maybe s et préparer l'arbre pour la prochaine étape. Il y a d'autres façons Functor, Foldable et Traversable sont utiles donc ce sont un must à avoir.

À ce stade, j'ai pensé que ce que je veux est probablement pas possible sans métaprogrammation donc j'ai cherché un modèle de solution de haskell. Bien sûr, il y a la bibliothèque genifunctors qui implémente les fonctions génériques fmap, foldMap et traverse. L'utilisation de ces il est une simple question d'écrire quelques emballages de New Type pour faire différentes instances de classes de types requis autour des paramètres appropriés:

fmapGAst = $(genFmap Ast) 
foldMapGAst = $(genFoldMap Ast) 
traverseGast = $(genTraverse Ast) 

newtype OverT1 t2 t3 t1 = OverT1 {unwrapT1 :: Ast t1 t2 t3 } 
newtype OverT2 t1 t3 t2 = OverT2 {unwrapT2 :: Ast t1 t2 t3 } 
newtype OverT3 t1 t2 t3 = OverT3 {unwrapT3 :: Ast t1 t2 t3 } 

instance Functor (OverT1 a b) where 
    fmap f w = OverT1 $ fmapGAst f id id $ unwrapT1 w 

instance Functor (OverT2 a b) where 
    fmap f w = OverT2 $ fmapGAst id f id $ unwrapT2 w 

instance Functor (OverT3 a b) where 
    fmap f w = OverT3 $ fmapGAst id id f $ unwrapT3 w 

..