有没有办法阻止Data.Generics.Alloy.GenInstances扫描Data.Text.Internal?

时间:2018-06-11 03:21:00

标签: haskell

我需要对AST进行转换;这是AST的一部分:

data Expr
  = BinExpr { beOp    :: BinaryOp
            , beLeft  :: Expr
            , beRight :: Expr }
  | Name Text
  | IntegerLit Integer
  | StringLit Text
  deriving (Data, Typeable)

这是一个相当复杂的AST,因此涉及很多类型。

我使用alloy生成通用转换,具体为:

autoGen :: IO ()
autoGen = do
  createDirectoryIfMissing True baseDir
  writeInstancesTo inst doc imports targetFile
  where
    inst = allInstances GenWithoutOverlapped
    doc = [genInstance (undefined :: Doc)]
    imports = header ++ instanceImports

现在,使用String时这很好,但我尝试迁移到Data.Text。当代码生成运行时,它会像这样读入Data.Text的内部:

instance (Alloy ([(GHC.Types.Char)]) (f :- ops) BaseOp) =>
     Alloy ((Data.Text.Internal.Text)) BaseOp (f :- ops) where
  transform _ ops (Data.Text.Internal.pack a0)
      =  Data.Text.Internal.pack
     (transform ops BaseOp (a0))

我认为pack与GHC内部结构相关联,因此不是有效的模式匹配,无论如何,使用Data.Text内部的代码都会破坏不变量。 (编辑:它看起来像是instance Data Text where gfoldl f z txt = z pack f (unpack txt)声明,但无论如何,我都不需要/想要遍历文字值。)

有没有办法强迫Alloy将类型视为原子?我希望避免使用newtype来包装Text,因为所有使用AST的代码都需要处理它,这相当违背了使用泛型来避免样板的目的。

1 个答案:

答案 0 :(得分:2)

也许尝试这个技巧:我们参数化Expr类型,以便在使用合金派生实例时覆盖用于Data的{​​{1}}实例。

Text

代码库的其余部分可以使用此同义词,希望不会因类型推断问题而破坏太多。

data Expr_ text
  = BinExpr { beOp    :: BinaryOp
            , beLeft  :: Expr_ text
            , beRight :: Expr_ text }
  | Name text
  ...
  | StringLit text

但是对于type Expr = Expr_ Text - 泛型操作,我们在Data周围使用newtype包装,并使其表现得像一个无效的构造函数,希望合金不需要{{1}的结果(或者你可以使用模式同义词使其行为像一个字符串)。

Text
然后

gunfold会专注于newtype DataText = DataText Text instance Data DataText where gunfold _ f _ = f undefined ... 的所有内容。

使用autoGen轻松转换DummyTextData.Coerce.coerce上的功能。

Expr_ DataText

这可能用于根据从您派生的实例为Expr编写合金类型类的实例。它有点样板,但希望它可以被包含和隐藏,而不会影响代码的其余部分。