Ich mag die Idee wirklich mit catamorphisms/anamorphisms in allgemeiner Weise zu arbeiten, aber es scheint mir, es eine bedeutende Leistung Nachteil hat:Kann GHC generische Funktionen wie Katamorphismen optimieren (entforsten)?
Angenommen, wir wollen mit einer Baumstruktur in dem kategorischen Weg zur Arbeit - zu beschreiben, unterschiedliche Faltung unter Verwendung eines generischen catamorphism function:
newtype Fix f = Fix { unfix :: f (Fix f) }
data TreeT r = Leaf | Tree r r
instance Functor TreeT where
fmap f Leaf = Leaf
fmap f (Tree l r) = Tree (f l) (f r)
type Tree = Fix TreeT
catam :: (Functor f) => (f a -> a) -> (Fix f -> a)
catam f = f . fmap (catam f) . unfix
Jetzt können wir Funktionen schreiben wie:
depth1 :: Tree -> Int
depth1 = catam g
where
g Leaf = 0
g (Tree l r) = max l r
Leider hat dieser Ansatz einen wesentlichen Nachteil: Dur Bei der Berechnung werden neue Instanzen von TreeT Int
auf jeder Ebene in erstellt, nur um sofort von g
verbraucht zu werden. Im Vergleich zur klassischen Definition
depth2 :: Tree -> Int
depth2 (Fix Leaf) = 0
depth2 (Fix (Tree l r)) = max (depth1 l) (depth1 r)
unsere depth1
wird immer langsamer machen unnötige Belastung für den GC. Eine Lösung wäre, hylomorphisms zu verwenden und Erstellungs- und Faltbäume zusammen zu kombinieren. Aber oft wollen wir das nicht, wir möchten vielleicht, dass ein Baum an einer Stelle erstellt wird und dann an einer anderen Stelle weitergegeben wird, um später gefaltet zu werden. Oder mehrere Ordner mit verschiedenen Katamorphismen zu haben.
Gibt es eine Möglichkeit, GHC zu optimieren depth1
? Etwas wie Inlining catam g
und dann fusing/deforestingg . fmap ...
innen?
Ich bin zu spät zu dieser Party, aber sollte es nicht irgendwo im 'Tree'-Fall von' g' (oder 'depth2') eine '+ 1' geben, damit die Funktion die Tiefe des Baumes berechnen kann? Ansonsten kann ich nicht sehen, wie 'depth1' oder' depth2' alles andere als null zurückgeben kann. –
Außerdem denke ich, dass "depth1" in depth2's Definition eigentlich "depth2" sein sollte. –