【问题标题】:Deriving extensions with multiparameter types使用多参数类型派生扩展
【发布时间】:2017-06-05 10:06:01
【问题描述】:

我有一个 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 的最小化版本以及您想要派生的一些类型参数和类型类?
  • 这是个好主意。我的直觉告诉我你已经超出了自动推导机制的能力,是时候开始关注generics了。
  • @luqui 这也是我的结论。派生语言扩展的存在是有原因的,在这种情况下它们似乎不够强大。

标签: haskell types deriving language-extension


【解决方案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, FoldableTraversable 还有其他有用的方法,所以这些是必须具备的。

此时我想我想要的很可能是不可能的 没有元编程,所以我搜索了一个模板 haskell 解决方案。 果然有实现泛型的genifunctorsfmap, 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

..

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-07-20
    • 2023-04-09
    • 2022-11-25
    • 1970-01-01
    • 1970-01-01
    • 2021-05-17
    • 2013-04-27
    • 1970-01-01
    相关资源
    最近更新 更多