mi piace molto l'idea di lavorare con catamorphisms/anamorphisms in modo generico, ma mi sembra che ha un notevole svantaggio delle prestazioni:È possibile rendere GHC ottimizzare (deforest) funzioni generiche come i catamorfismi?
supponiamo di voler lavorare con una struttura ad albero in modo categorico - per descrivere differente piegatura usando un generico 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
Ora possiamo scrivere funzioni quali:
depth1 :: Tree -> Int
depth1 = catam g
where
g Leaf = 0
g (Tree l r) = max l r
Sfortunatamente, questo approccio ha uno svantaggio significativo: Dur Durante il calcolo, le nuove istanze di TreeT Int
vengono create ad ogni livello in fmap
per essere immediatamente consumate da g
. Rispetto alla definizione classica
depth2 :: Tree -> Int
depth2 (Fix Leaf) = 0
depth2 (Fix (Tree l r)) = max (depth1 l) (depth1 r)
nostro depth1
sarà sempre più lento facendo uno sforzo eccessivo del GC. Una soluzione sarebbe quella di utilizzare hylomorphisms e combinare insieme la creazione e la piegatura degli alberi. Ma spesso non vogliamo farlo, potremmo voler creare un albero in un posto e poi passare da qualche altra parte per essere piegato in seguito. Oppure, per essere cartelle diverse volte con diversi catamorfismi.
C'è un modo per ottimizzare GHC depth1
? Qualcosa come l'inline catam g
e poi fusing/deforestingg . fmap ...
all'interno?
Sono in ritardo per questa festa, ma non dovrebbe esserci un '+ 1' da qualche parte nel caso' Tree' di 'g' (o' depth2') per la funzione per calcolare la profondità dell'albero? Altrimenti, non vedo come 'depth1' o' depth2' possano restituire altro che zero. –
Inoltre, penso che 'depth1' dovrebbe essere' depth2' nella definizione di 'depth2'. –