Ich werde versuchen zeigen, dass dies für bestimmte GADTs getan werden kann, mit Ihrem GADT als ein Beispiel.
Ich werde das Data.Reify Paket verwenden. Dazu muss ich eine neue Datenstruktur definieren, in der die rekursiven Positionen durch einen Parameter ersetzt werden.
Beachten Sie, dass ich eine Menge von Typinformationen entfernen, die im ursprünglichen GADT verfügbar waren. Für die ersten drei Konstruktoren ist klar, was der zugehörige Typ war (Int, Int und Bool). Für den letzten werde ich den Typ mit TypeRep erinnern (verfügbar in Data.Typeable). Die für das Paket reeify benötigte Instanz für MuRef ist unten aufgeführt.
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
Jetzt können wir reifyGraph verwenden Sharing zu erholen. Viele Informationen gingen jedoch verloren. Versuchen wir, es wiederherzustellen.Ich änderte Ihre Definition von AST2 leicht:
data Ast2 e where
IntLit2 :: Int -> Ast2 Int
Add2 :: Unique -> Unique -> Ast2 Int
BoolLit2 :: Bool -> Ast2 Bool
IfThenElse2 :: Unique -> Unique -> Unique -> Ast2 e
Der Graph aus dem reify Paket sieht wie folgt aus (wo e = AstNode):
data Graph e = Graph [(Unique, e Unique)] Unique
Ermöglicht eine neue Graphdatenstruktur machen, wo Wir können Ast2 Int und Ast2 Bool separat speichern (also, wo die Typinformationen wiederhergestellt wurden):
data Graph2 = Graph2 [(Unique, Ast2 Int)] [(Unique, Ast2 Bool)] Unique
deriving Show
Jetzt müssen wir nur eine Funktion von Graph AstNode (das Ergebnis von reifyGraph) zu Graph2 finden:
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
wir ein Beispiel tun:
expr = Add (IntLit 42) expr
test = do
graph <- reifyGraph expr
print graph
print $ recoverTypes graph
Drucke:
let [(1,AddN 2 1),(2,IntLitN 42)] in 1
Graph2 [(1,Add2 2 1),(2,IntLit2 42)] [] 1
Die erste Zeile zeigt uns, dass retifyGraph Freigabe korrekt wiederhergestellt hat. Die zweite Zeile zeigt uns, dass nur Ast2 Int Typen gefunden wurden (was auch korrekt ist).
Diese Methode ist leicht anpassbar für andere spezifische GADTs, aber ich sehe nicht, wie es vollständig generisch gemacht werden könnte.
Der vollständige Code kann unter http://pastebin.com/FwQNMDbs gefunden werden.
Ich machte mein Beispiel ein wenig schwierig zu folgen, indem ich die gleichen Namen für die Konstruktoren beider Datentypen verwendet. Ich habe sie umbenannt, um mehr Sinn zu machen. Es sieht so aus, als hättest du das schon in deinem Code gemacht. – tibbe
@ Sjoerd-visscher Ich glaube, die Lösung (zumindest die mit 'Typable') hat ein kleines Problem: Es behindert die Freigabe-Analyse. Ich weiß nicht, ob dies auf den zusätzlichen Wrap-Konstruktor oder auf etwas anderes zurückzuführen ist. Aber mein Geld ist auf dem Wrap Konstruktor. Irgendwelche Ideen? –
@AlessandroVermeulen Offen gesagt weiß ich nichts über das Teilen. Wer/was macht die Share-Analyse? –