我需要对AST进行转换; 这是AST的一部分:
data Expr
= BinExpr { beOp :: BinaryOp
, beLeft :: Expr
, beRight :: Expr }
| Name Text
| IntegerLit Integer
| StringLit Text
deriving (Data, Typeable)
Run Code Online (Sandbox Code Playgroud)
这是一个相当复杂的AST,所以涉及很多类型.
我正在使用合金来生成通用转换,具体来说:
autoGen :: IO ()
autoGen = do
createDirectoryIfMissing True baseDir
writeInstancesTo inst doc imports targetFile
where
inst = allInstances GenWithoutOverlapped
doc = [genInstance (undefined :: Doc)]
imports = header ++ instanceImports
Run Code Online (Sandbox Code Playgroud)
现在,使用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))
Run Code Online (Sandbox Code Playgroud)
我认为pack它与GHC内部结构相关联,因此不是有效的模式匹配,并且无论如何,使用Data.Text内部的代码都会破坏不变量.(编辑:看起来有一个instance Data Text where gfoldl f z txt = z packf (unpack txt)声明,但无论如何,我不需要/想要遍历文本值.)
有没有办法强迫Alloy将类型视为原子?我希望避免使用newtype来包装Text,因为所有使用AST的代码都需要处理它,这相当违背了使用泛型来避免样板的目的.
也许可以尝试这个技巧:我们参数化Expr类型以覆盖在使用合金派生实例时Data使用的实例。Text
data Expr_ text
= BinExpr { beOp :: BinaryOp
, beLeft :: Expr_ text
, beRight :: Expr_ text }
| Name text
...
| StringLit text
Run Code Online (Sandbox Code Playgroud)
代码库的其余部分可以使用这个同义词,希望不会因为类型推断问题而破坏太多。
type Expr = Expr_ Text
Run Code Online (Sandbox Code Playgroud)
但对于Data泛型操作,我们使用newtype包装器Text并使其表现得像无效构造函数,希望合金不需要结果gunfold(或者也许您可以使用模式同义词使其表现得像字符串)。
newtype DataText = DataText Text
instance Data DataText where
gunfold _ f _ = f undefined
...
Run Code Online (Sandbox Code Playgroud)
autoGen然后将专注于 的一切DummyText。
用于在和Data.Coerce.coerce上的函数之间轻松转换。Expr_ DataTextExpr
coerce :: Expr_ DataText -> Expr
coerce :: Expr -> Expr_ DataText
coerce :: (Expr_ DataText -> Expr_ DataText) -> Expr -> Expr
Run Code Online (Sandbox Code Playgroud)
这可能用于Expr根据从您派生的实例来编写合金类型类的实例。这是一些样板文件,但希望它可以被包含和隐藏,而不影响其余的代码。