我需要对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的代码都需要处理它,这相当违背了使用泛型来避免样板的目的。
答案 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
轻松转换DummyText
和Data.Coerce.coerce
上的功能。
Expr_ DataText
这可能用于根据从您派生的实例为Expr
编写合金类型类的实例。它有点样板,但希望它可以被包含和隐藏,而不会影响代码的其余部分。