3

我有一个 Ast 类型的构造函数,由标识符类型参数化。使用 DeriveFunctor、DeriveFoldable 和 DeriveTraversable 扩展,可以自动创建适当的实例。

现在我发现引入更多类型参数很有用,但不幸的是上述方法无法扩展。理想情况下,我希望能够将我的 Ast 类型包装在选择类型中,这将允许我映射到适当的类型参数。有什么方法可以实现类似的效果而不必自己定义实例?

编辑:

这是原始 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)

..
4

1 回答 1

1

折腾了一整天,我得出以下结论:

问题中提出的 Ast 结果证明不是很灵活。在后期阶段,我想以无法仅通过参数化原始 Ast 来表达的方式注释不同的节点。

所以我改变了 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 |
    ..

现在我可以简单地为每个编译器阶段定义一个新类型链。重命名器阶段的 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) 
}

在类型检查阶段,Ast 可以由节点中使用的不同内部类型参数化。

这种参数化允许Maybe在每个阶段的中间构造带有包装参数的 Asts。

如果一切正常,我们可以使用fmap删除Maybes 并为下一阶段准备树。还有其他方法Functor, Foldable并且Traversable很有用,所以这些是必须的。

在这一点上,我认为如果没有元编程,我想要的很可能是不可能的,所以我搜索了一个模板 haskell 解决方案。果然有实现泛型 和函数的genifunctors库。使用这些,编写一些新类型包装器以围绕适当的参数创建所需类型类的不同实例是一件简单的事情:fmap, foldMaptraverse

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

..
于 2017-06-06T05:35:08.630 回答