我會試着證明這可以用特定的GADT來完成,以你的GADT爲例。我將使用Data.Reify包。這需要我定義一個新的數據結構,其中recusive位置被一個參數替代。
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
從物化包中的圖形看起來像這樣(其中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
現在,我們只需要從圖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,但我不明白它如何完全通用。
完整的代碼可以在http://pastebin.com/FwQNMDbs找到。
我讓我的例子有點難以遵循使用相同的名稱爲兩種數據類型的構造函數。我將它們重命名爲更有意義。它看起來像你已經在你的代碼中做到了。 – tibbe
@ Sjoerd-visscher我相信解決方案(至少是使用'Typeable'的解決方案)有一個小問題:它阻礙了共享分析。我不知道這是由於額外的Wrap構造函數還是由於其他原因。然而我的錢在Wrap構造函數上。有任何想法嗎? –
@AlessandroVermeulen坦率地說,我對分享一無所知。誰/共享分析是什麼? –