简体   繁体   English

使用多参数类型派生扩展

[英]Deriving extensions with multiparameter types

I have an Ast type constructor, parameterized by the identifier type. 我有一个Ast类型的构造函数,由标识符类型参数化。 Using the DeriveFunctor, DeriveFoldable and DeriveTraversable extensions it is possible to automatically create the appropriate instances. 使用DeriveFunctor,DeriveFoldable和DeriveTraversable扩展,可以自动创建适当的实例。

Now I find it useful to introduce more type parameters but unfortunately the above method doesn't scale. 现在我发现引入更多类型参数很有用,但遗憾的是上述方法无法扩展。 Ideally I would like to be able to wrap my Ast type in selection types which would allow me to fmap to the appropriate type parameters. 理想情况下,我希望能够将Ast类型包装在选择类型中,这将允许我fmap到适当的类型参数。 Is there some way to achieve a similar effect without having to define the instances myself? 有没有办法实现类似的效果,而无需自己定义实例?

edit: 编辑:

Here is a small example of what the original Ast looked like: 这是原始Ast看起来像的一个小例子:

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)

..

After fiddling around all day I came to the following conclusions: 在整天摆弄后,我得出以下结论:

The Ast presented in the question turned out not to be very flexible. 在问题中提出的Ast结果不是很灵活。 In later stages I want to annotate different nodes in a way that can't be expressed by just parameterizing the original Ast. 在后面的阶段,我想以一种只能通过参数化原始Ast来表达的方式来注释不同的节点。

So I changed the Ast in order to act as a base for writing new types: 所以我改变了Ast,以便成为编写新类型的基础:

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 |
    ..

Now I can simply define a chain of newtypes for each compiler stage. 现在我可以简单地为每个编译器阶段定义一个newtypes链。 The Ast at the stage of the renamer may be parameterized around the identifier type: 重命名阶段的Ast可以围绕标识符类型进行参数化:

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) 
}

During the typechecking stage the Ast may be parameterized by the different internal types used in the nodes. 在类型检查阶段期间,可以通过节点中使用的不同内部类型来参数化Ast。

This parameterization allows for constructing Asts with Maybe wrapped parameters in the middle of each stage. 此参数化允许在每个阶段的中间构建带有Maybe包装参数的Asts。

If everything is ok we can use fmap to remove the Maybe s and prepare the tree for the next stage. 如果一切正常,我们可以使用fmap删除Maybe s并为下一阶段准备树。 There are other ways Functor, Foldable and Traversable are useful so these are a must to have. Functor, FoldableTraversable还有其他方法可用,所以这些都是必须的。

At this point I figured that what I want is most likely not possible without metaprogramming so I searched for a template haskell solution. 在这一点上,我认为没有元编程,我想要的东西很可能是不可能的,所以我搜索了模板haskell解决方案。 Sure enough there is the genifunctors library which implements generic fmap, foldMap and traverse functions. 果然有genifunctors库实现了通用的fmap, foldMaptraverse函数。 Using these it's a simple matter of writing a few newtype wrappers to make different instances of the required typeclasses around the appropriate parameters: 使用这些是一个简单的问题,编写一些newtype包装器,围绕相应的参数创建所需类型类的不同实例:

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

..

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM