Так что на самом деле это ссылка на статью Мейера и нескольких других под названием « Функциональное программирование с использованием бананов, линз, конвертов и колючей проволоки », основная идея в том, что мы можем взять любой рекурсивный тип данных, например, скажем,
data List = Cons Int List | Nil
и мы можем выделить рекурсию в переменную типа
data ListF a = Cons Int a | Nil
причина, по которой я добавил это F
потому, что теперь это функтор! Это также позволяет нам имитировать списки, но с изюминкой: для создания списков мы должны вложить тип списка
type ThreeList = ListF (ListF (ListF Void)))
Чтобы восстановить наш первоначальный список, мы должны продолжать вкладывать его бесконечно . Это даст нам тип, ListFF
где
ListF ListFF == ListFF
Для этого определите «тип с фиксированной точкой»
data Fix f = Fix {unfix :: f (Fix f)}
type ListFF = Fix ListF
В качестве упражнения вы должны убедиться, что это удовлетворяет нашему уравнению, приведенному выше. Теперь мы наконец можем определить, что такое бананы (катаморфизмы)!
type ListAlg a = ListF a -> a
ListAlg
s являются типом «списка алгебр», и мы можем определить конкретную функцию
cata :: ListAlg a -> ListFF -> a
cata f = f . fmap (cata f) . unfix
Дальше больше
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
Выглядит знакомо? cata
точно так же, как правильные сгибы!
Что действительно интересно, так это то, что мы можем сделать это не только над списками, у любого типа, который определен с помощью этой «фиксированной точки функтора», есть cata
и, чтобы вместить их все, мы просто должны ослабить сигнатуру типа.
cata :: (f a -> a) -> Fix f -> a
Это на самом деле вдохновлено частью теории категорий, о которой я писал , но это мясо стороны Haskell.