Tôi sẽ cố gắng cho thấy rằng điều này có thể được thực hiện cho các GADT cụ thể, sử dụng GADT làm ví dụ.
Tôi sẽ sử dụng gói Data.Reify. Điều này đòi hỏi tôi phải xác định một cấu trúc dữ liệu mới, trong đó các vị trí recusive được thay thế bởi một tham số.
data AstNode s where
IntLitN :: Int -> AstNode s
AddN :: s -> s -> AstNode s
BoolLitN :: Bool -> AstNode s
IfThenElseN :: TypeRep -> s -> s -> s -> AstNode s
Lưu ý rằng tôi xóa rất nhiều thông tin loại có sẵn trong GADT gốc. Đối với ba hàm tạo đầu tiên, rõ ràng loại liên kết là gì (Int, Int và Bool). Đối với người cuối cùng tôi sẽ nhớ loại sử dụng TypeRep (có sẵn trong Data.Typeable). Ví dụ cho MuRef, theo yêu cầu của gói thống nhất, được hiển thị bên dưới.
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
Bây giờ chúng tôi có thể sử dụng reifyGraph để khôi phục chia sẻ. Tuy nhiên, rất nhiều loại thông tin đã bị mất. Hãy thử phục hồi nó.Tôi thay đổi định nghĩa của bạn về Ast2 hơi:
data Ast2 e where
IntLit2 :: Int -> Ast2 Int
Add2 :: Unique -> Unique -> Ast2 Int
BoolLit2 :: Bool -> Ast2 Bool
IfThenElse2 :: Unique -> Unique -> Unique -> Ast2 e
Đồ thị từ gói cụ thể hóa trông như thế này (nơi e = AstNode):
data Graph e = Graph [(Unique, e Unique)] Unique
Cho phép tạo ra một cấu trúc dữ liệu đồ thị mới, nơi chúng tôi có thể lưu trữ Ast2 Int và Ast2 Bool riêng biệt (do đó, nơi thông tin về loại đã được khôi phục):
data Graph2 = Graph2 [(Unique, Ast2 Int)] [(Unique, Ast2 Bool)] Unique
deriving Show
Bây giờ chúng ta chỉ cần tìm một hàm từ Graph AstNode (kết quả của 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
Cho phép làm một ví dụ:
expr = Add (IntLit 42) expr
test = do
graph <- reifyGraph expr
print graph
print $ recoverTypes graph
Bản in:
let [(1,AddN 2 1),(2,IntLitN 42)] in 1
Graph2 [(1,Add2 2 1),(2,IntLit2 42)] [] 1
Dòng đầu tiên cho chúng ta thấy rằng reifyGraph đã chia sẻ chính xác chia sẻ. Dòng thứ hai cho chúng ta thấy rằng chỉ có các loại Ast2 Int được tìm thấy (cũng đúng).
Phương pháp này có thể dễ dàng thích ứng với các GADT cụ thể khác, nhưng tôi không thấy nó có thể được làm hoàn toàn chung chung như thế nào.
Mã hoàn chỉnh có thể được tìm thấy tại http://pastebin.com/FwQNMDbs.
Tôi đã làm ví dụ của mình hơi khó làm theo bằng cách sử dụng cùng tên cho các nhà xây dựng của cả hai loại dữ liệu. Tôi đã đổi tên chúng thành ý nghĩa hơn. Có vẻ như bạn đã làm điều đó trong mã của mình. – tibbe
@ Sjoerd-visscher Tôi tin rằng giải pháp (ít nhất là bằng cách sử dụng 'Typeable') có một vấn đề nhỏ: nó cản trở việc phân tích chia sẻ. Tôi không biết liệu điều này là do việc xây dựng thêm Wrap hoặc do cái gì khác. Tuy nhiên tiền của tôi là trên các nhà xây dựng Wrap. Bất kỳ ý tưởng? –
@AlessandroVermeulen Thành thật mà nói tôi không biết gì về việc chia sẻ. Phân tích chia sẻ là ai? –