我将尝试以您的 GADT 为例,证明这可以针对特定的 GADT 完成。
我将使用Data.Reify包。这需要我定义一个新的数据结构,其中递归位置被一个参数替换。
data AstNode s where
IntLitN :: Int -> AstNode s
AddN :: s -> s -> AstNode s
BoolLitN :: Bool -> AstNode s
IfThenElseN :: TypeRep -> s -> s -> s -> AstNode s
请注意,我删除了许多原始 GADT 中可用的类型信息。对于前三个构造函数,关联类型是什么(Int、Int 和 Bool)很清楚。对于最后一个,我会记住使用TypeRep的类型(在Data.Typeable中可用)。reify包所需的 MuRef实例如下所示。
instance Typeable e => MuRef (Ast e) where
type DeRef (Ast e) = AstNode
mapDeRef f (IntLit a) = pure $ IntLitN a
mapDeRef f (Add a b) = AddN <$> f a <*> f b
mapDeRef f (BoolLit a) = pure $ BoolLitN a
mapDeRef f (IfThenElse a b c :: Ast e) =
IfThenElseN (typeOf (undefined::e)) <$> f a <*> f b <*> f c
现在我们可以使用reifyGraph来恢复共享。但是,丢失了很多类型信息。让我们尝试恢复它。我稍微改变了你对Ast2的定义:
data Ast2 e where
IntLit2 :: Int -> Ast2 Int
Add2 :: Unique -> Unique -> Ast2 Int
BoolLit2 :: Bool -> Ast2 Bool
IfThenElse2 :: Unique -> Unique -> Unique -> Ast2 e
reify 包中的图表如下所示(其中e = AstNode):
data Graph e = Graph [(Unique, e Unique)] Unique
让我们创建一个新的图形数据结构,我们可以在其中分别存储Ast2 Int和Ast2 Bool(因此,类型信息已恢复):
data Graph2 = Graph2 [(Unique, Ast2 Int)] [(Unique, Ast2 Bool)] Unique
deriving Show
现在我们只需要从Graph AstNode(reifyGraph 的结果)到Graph2中找到一个函数:
recoverTypes :: Graph AstNode -> Graph2
recoverTypes (Graph xs x) = Graph2 (catMaybes $ map (f toAst2Int) xs)
(catMaybes $ map (f toAst2Bool) xs) x where
f g (u,an) = do a2 <- g an
return (u,a2)
toAst2Int (IntLitN a) = Just $ IntLit2 a
toAst2Int (AddN a b) = Just $ Add2 a b
toAst2Int (IfThenElseN t a b c) | t == typeOf (undefined :: Int)
= Just $ IfThenElse2 a b c
toAst2Int _ = Nothing
toAst2Bool (BoolLitN a) = Just $ BoolLit2 a
toAst2Bool (IfThenElseN t a b c) | t == typeOf (undefined :: Bool)
= Just $ IfThenElse2 a b c
toAst2Bool _ = Nothing
让我们做一个例子:
expr = Add (IntLit 42) expr
test = do
graph <- reifyGraph expr
print graph
print $ recoverTypes graph
印刷:
let [(1,AddN 2 1),(2,IntLitN 42)] in 1
Graph2 [(1,Add2 2 1),(2,IntLit2 42)] [] 1
第一行显示reifyGraph已正确恢复共享。第二行告诉我们只找到了Ast2 Int类型(这也是正确的)。
这种方法很容易适用于其他特定的 GADT,但我看不出它是如何完全通用的。
The complete code can be found at http://pastebin.com/FwQNMDbs .