【问题标题】:AST conversion in HaskellHaskell 中的 AST 转换
【发布时间】:2015-01-31 23:41:45
【问题描述】:

我有一个 AST,其根节​​点的类型为 E.Root。我需要将其转换为具有类型为I.Root 的根节点的AST。

我现在可以定义一个带有类型签名的函数eToI

eToI :: E.Root -> AdditionalInfo -> I.Root

但是,两个 AST 共享很多节点。所以,eToF 函数有很多样板代码,从 E's 节点构建 I's 节点基本相同。

我想解决这两个问题:

  1. 在类型级别,避免定义I 的节点。我有所有节点 E 的定义。我在I 中定义了哪些节点发生了变化。我可以拥有吗 编译器生成I 中的所有类型,通过某种方式映射什么 需要改变吗?

  2. 在价值层面,我只想为 变化的节点(假设 E 的 A 映射到 I 的 Z ..):

        aToZ :: E.A -> AdditionalInfo -> I.Z
        bToY :: E.B -> AdditionalInfo -> I.Y
    

    现在,编译器可以生成像 eToI 这样的函数吗?

        eToI :: E.Root -> AdditionalInfo -> I.Root
    

Haskell 的惯用方法是什么?

【问题讨论】:

  • 两种 AST 类型有什么不同?
  • 废掉你的样板,也许吧?

标签: haskell


【解决方案1】:

有时您可以将E.RootI.Root 的共同方面分解为一种或多种可重用的数据类型。它通常还有助于将 AdditionalInfo -> ... 隐藏在 monad 或应用函子中。例如,这里有一些伪代码:

module Common where
  {-# LANGUAGE DeriveTraversable #-}
  data Reusable root = ... root ...
    deriving (Functor, Foldable, Traversable)

module E where
  import Common
  data Root = Leaf (Reusable Root) | Node Root

module I where
  {-# LANGUAGE GeneralizedNewtypeDeriving #-}
  import Common

  import Data.Monoid

  data Root = Root [Reusable Root]
    deriving Monoid

module Transform where
  import Common
  import qualified E
  import qualified I

  import Control.Applicative
  import Control.Monad.Reader
  import Data.Monoid

  type AdditionalInput = ...
  type F = Reader AdditionalInput

  convertRoot :: E.Root -> F I.Root
  convertRoot (Leaf reusable) =
    traverse convertRoot reusable
  convertRoot (Node left right) =
    liftA2 mappend (convertRoot left) (convertRoot right)

现在我可以使用traverseReusable E.RootReusable I.Root 之间进行转换。

【讨论】:

  • 我认为 OP 的意图是 1)通过不为 I 编写 type 声明 来减少样板(因为 I 的大部分 AST 与 E 相似),然后 2)只写将I值显式转换为E值的函数(对于少数不同的类型),其余的不写。你的代码可能是他已经拥有的。这个问题可以用 Template Haskell 解决,但可能很难看。
  • @tanmaig:如果我的回答中的方法完全适用,那么它可以实现这两个目标。特别是:(1)Reusable的类型声明只写一次,在E和I中没有两次。(2)有显式convertRoot,因为E.Root和I.Root不同,但是没有显式convertReusable,因为自动生成的遍历可以很好地完成这项工作。
  • 我明白,但我不知道 traverse 将如何处理具有多个类型参数而不仅仅是一个类型参数的“可重用”可遍历类型。比如说,AST 是:data Reusable aRoot bRoot cRoot 在这种情况下如何派生可遍历类型类?
  • @tanmaig:啊,这是个好问题。我同意在这种情况下,简单的traverse 不够好。
猜你喜欢
  • 2020-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多