我有一个 Ast 类型构造函数,由标识符类型参数化。 使用 DeriveFunctor、DeriveFoldable 和 DeriveTraversable 扩展 可以自动创建适当的实例。
现在我发现引入更多类型参数很有用,但不幸的是 上述方法无法扩展。理想情况下我希望能够 将我的 Ast 类型包装在选择类型中,这将允许我 fmap 到 适当的类型参数。有没有什么方法可以达到类似的效果 无需自己定义实例即可实现效果?
编辑:
以下是原始 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)
..
最佳答案
经过一整天的摆弄,我得出以下结论:
问题中提出的 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
包装来构造 Ast
每个阶段中间的参数。
如果一切正常,我们可以使用 fmap
删除 Maybe
并为下一阶段准备树。 Functor、Foldable
和 Traversable
还有其他一些有用的方法,因此这些是必须具备的。
此时我想我想要的很可能是不可能的
没有元编程,所以我搜索了模板 haskell 解决方案。
果然有genifunctors库实现了泛型
fmap、foldMap
和 traverse
函数。使用这些很简单
编写一些新类型包装器来围绕适当的参数创建所需类型类的不同实例:
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
..
关于haskell - 使用多参数类型派生扩展,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44366489/