Jest to tak naprawdę odniesienie do artykułu Meijera i kilku innych zatytułowanych „ Programowanie funkcjonalne z bananami, soczewkami, kopertami i drutem kolczastym ”, podstawową ideą jest to, że możemy wziąć dowolny typ danych rekurencyjnych, jak powiedzmy
data List = Cons Int List | Nil
i możemy rozliczyć rekurencję na zmienną typu
data ListF a = Cons Int a | Nil
powodem, dla którego dołączyłem, F
jest to, że jest to teraz funkt! Pozwala nam także naśladować listy, ale z pewnym zwrotem: aby budować listy, musimy zagnieżdżać typ listy
type ThreeList = ListF (ListF (ListF Void)))
Aby odzyskać naszą oryginalną listę, musimy zagnieżdżać ją w nieskończoność . To da nam typ ListFF
gdzie
ListF ListFF == ListFF
W tym celu zdefiniuj „stały typ punktu”
data Fix f = Fix {unfix :: f (Fix f)}
type ListFF = Fix ListF
Jako ćwiczenie powinieneś sprawdzić, czy spełnia to powyższe równanie, a teraz możemy wreszcie zdefiniować, czym są banany (katamorfizmy)!
type ListAlg a = ListF a -> a
ListAlg
s są rodzajem „algebr listowych” i możemy zdefiniować określoną funkcję
cata :: ListAlg a -> ListFF -> a
cata f = f . fmap (cata f) . unfix
Ponadto
cata :: ListAlg a -> ListFF -> a
cata :: (Either () (Int, a) -> a) -> ListFF -> a
cata :: (() -> a) -> ((Int, a) -> a) -> ListFF -> a
cata :: a -> (Int -> a -> a) -> ListFF -> a
cata :: (Int -> a -> a) -> a -> [Int] -> a
Wygląda podobnie? cata
jest dokładnie taki sam jak prawe fałdy!
Naprawdę interesujące jest to, że możemy to zrobić na więcej niż tylko listach, każdy typ zdefiniowany za pomocą tego „stałego punktu funktora” ma cata
i, aby pomieścić je wszystko, wystarczy rozluźnić podpis typu
cata :: (f a -> a) -> Fix f -> a
Jest to faktycznie zainspirowane fragmentem teorii kategorii, o której pisałem , ale jest to mięso strony Haskella.