bardzo podoba mi się pomysł pracy z catamorphisms/anamorphisms w sposób ogólny, ale wydaje mi się ona istotna wada wydajność:Czy możliwe jest optymalizowanie (deforest) ogólnych funkcji GHC, takich jak katamorfizmy?
Załóżmy, że chcemy pracować ze strukturą drzewa w kategoryczny sposób - do opisania inna składane za pomocą rodzajowy 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
teraz możemy napisać funkcje, takie jak:
depth1 :: Tree -> Int
depth1 = catam g
where
g Leaf = 0
g (Tree l r) = max l r
Niestety, takie podejście ma znaczącą wadę: Dur Podczas obliczeń nowe instancje TreeT Int
są tworzone na każdym poziomie w fmap
, aby natychmiast zostać zużyte przez g
. W porównaniu do klasycznej definicji
depth2 :: Tree -> Int
depth2 (Fix Leaf) = 0
depth2 (Fix (Tree l r)) = max (depth1 l) (depth1 r)
naszych depth1
zawsze będzie mniejsza co niepotrzebne obciążenie GC. Jednym z rozwiązań byłoby użycie hylomorphisms i połączenie ze sobą tworzenia i składania drzew. Ale często nie chcemy tego robić, możemy chcieć stworzyć drzewo w jednym miejscu, a następnie przekazać je gdzie indziej, aby później je złożyć. Lub, aby być folderem kilka razy z różnymi katamorfizmami.
Czy istnieje sposób na optymalizację GHC depth1
? Coś w stylu wstawiania catam g
, a następnie g . fmap ...
w środku?
Spóźniam się na to przyjęcie, ale czy nie powinno być gdzieś '+ 1' gdzieś w przypadku' drzewa' 'g' (lub' głębokości2') dla funkcji obliczania głębokości drzewa? W przeciwnym razie nie widzę, jak 'depth1' lub' depth2' mogą zwracać cokolwiek poza zerem. –
Ponadto, myślę, że 'depth1' powinien w rzeczywistości być' depth2' w definicji 'depth2'. –