Подтвердить что ты не робот

Когда в состав катаморфизмов входит катаморфизм?

Со страницы 3 http://research.microsoft.com/en-us/um/people/emeijer/Papers/meijer94more.pdf:

вообще не верно, что катаморфизмы замкнуты относительно композиции

В каких условиях катаморфизмы составляют катаморфизм? Более конкретно (при условии, что я правильно понял выражение):

Предположим, что у меня есть два базовых функтора F и G и складки для каждого: foldF :: (F a -> a) -> (μF -> a) и foldG :: (G a -> a) -> (μG -> a).

Теперь предположим, что у меня есть две алгебры a :: F μG -> μG и b :: G X -> X.

Когда композиция (foldG b) . (foldF a) :: μF -> X является катаморфизмом?


Изменить: У меня есть предположение, основанное на расширенном ответе dblhelix: что outG . a :: F μG -> G μG должен быть компонентом в μG некоторого естественного преобразования η :: F a -> G a. Я не знаю, правильно ли это. ( Изменить 2: Как указывает Кола, этого достаточно, но не нужно.)

Редактировать 3: Рен Торнтон в Haskell-Cafe добавляет: "Если у вас есть правильное свойство дистрибутивности (как подсказывает Кола), тогда все будет работать для конкретного случая. правильный тип свойства распределения обычно представляет собой естественное преобразование в какой-либо соответствующей категории, поэтому он просто откладывает вопрос о том, существует ли всегда соответствующая категория, и можем ли мы формализовать то, что означает" соответствующим образом связанный".

4b9b3361

Ответ 1

Когда состав (сгиб 2 г). (fold1 f) :: μF1 → А катаморфизм?

Когда существует F1 -algebra h :: F1 A → A такой, что fold1 h = fold2 g. fold1 f fold1 h = fold2 g. fold1 f.

Чтобы увидеть, что катаморфизмы в общем случае не являются замкнутыми по составу, рассмотрим следующие общие определения фиксированной точки, алгебры и катаморфизма на уровне типов:

newtype Fix f = In {out :: f (Fix f)}

type Algebra f a = f a -> a

cata :: Functor f => Algebra f a -> Fix f -> a
cata phi = phi . fmap (cata phi) . out

Для составления катаморфизмов нам понадобится

algcomp ::  Algebra f (Fix g) -> Algebra g a -> Algebra f a

Теперь попробуйте написать эту функцию. Он принимает две функции в качестве аргументов (типа f (Fix g) → Fix g и ga → a соответственно) и значение типа fa, и ему необходимо получить значение типа a. Как бы Вы это сделали? Чтобы получить значение типа a ваша единственная надежда - применить функцию типа ga → a, но тогда мы застряли: у нас нет средств для преобразования значения типа fa в значение типа ga, не так ли?

Я не уверен, имеет ли это какое-либо значение для ваших целей, но пример условия, при котором можно составить катаморфизм, - это если мы имеем морфизм от результата второго ката до фиксированной точки второго функтора:

algcomp' :: (Functor f, Functor g) =>
            (a -> Fix g) -> Algebra f (Fix g) -> Algebra g a -> Algebra f a
algcomp' h phi phi' = cata phi' . phi . fmap h

Ответ 2

(Отказ от ответственности: это выходит за рамки моей компетенции. Я считаю, что я прав (с предостережениями, предоставленными в разных точках), но... убедитесь сами.)

Катаморфизм можно рассматривать как функцию, заменяющую конструкторы типа данных другими функциями.

(В этом примере я буду использовать следующие типы данных:

data [a] = [] | a : [a]

data BinTree a = Leaf a | Branch (BinTree a) (BinTree a)

data Nat = Zero | Succ Nat

)

Например:

length :: [a] -> Nat
length = catamorphism
     []   -> 0
     (_:) -> (1+)

(К сожалению, синтаксис catamorphism {..} недоступен в Haskell (я видел что-то похожее в Pola). Я имел в виду написать для него квазивокатор.)

Итак, что такое length [1,2,3]?

length [1,2,3]
length (1 : 2 : 3 : [])
length (1:  2:  3:  [])
        1+ (1+ (1+ (0 )))
        3

Тем не менее, по причинам, которые станут очевидными позже, лучше определить его как тривиально эквивалентное:

length :: [a] -> Nat
length = catamorphism
     []   -> Zero
     (_:) -> Succ

Рассмотрим еще несколько примеров катаморфизмов:

map :: (a -> b) -> [a] -> b
map f = catamorphism
     []   -> []
     (a:) -> (f a :)

binTreeDepth :: Tree a -> Nat
binTreeDepth = catamorphism
     Leaf _ -> 0
     Branch -> \a b -> 1 + max a b

binTreeRightDepth :: Tree a -> Nat
binTreeRightDepth = catamorphism
     Leaf _ -> 0
     Branch -> \a b -> 1 + b

binTreeLeaves :: Tree a -> Nat
binTreeLeaves = catamorphism
     Leaf _ ->  1
     Branch -> (+)

double :: Nat -> Nat
double = catamorphism
     Succ -> Succ . Succ
     Zero -> Zero

Многие из них могут быть красиво составлены для формирования новых катаморфизмов. Например:

double . length . map f = catamorphism
     []   -> Zero
     (a:) -> Succ . Succ

double . binTreeRightDepth = catamorphism
     Leaf a -> Zero
     Branch -> \a b -> Succ (Succ b)

double . binTreeDepth также работает, но это почти чудо, в определенном смысле.

double . binTreeDepth = catamorphism
     Leaf a -> Zero
     Branch -> \a b -> Succ (Succ (max a b))

Это работает только потому, что double распределяется по max... Это чистое совпадение. (То же самое верно с double . binTreeLeaves.) Если мы заменили max тем, что не играло так хорошо с удвоением... Ну, давайте определим себя новым другом (который не ладит вместе с другие). Для двоичных операторов, которые double не распространяется, мы будем использовать (*).

binTreeProdSize :: Tree a -> Nat
binTreeProdSize = catamorphism
     Leaf _ -> 0
     Branch -> \a b -> 1 + a*b

Попробуем установить достаточные условия для двух составляющих двух катаморфизмов. Очевидно, что любой катаморфизм будет довольно счастливо скомпонован с length, double и map f, потому что они дают свою структуру данных, не глядя на результаты ребенка. Например, в случае length вы можете просто заменить Succ и Zero тем, что вы хотите, и у вас есть новый катаморфизм.

  • Если первый катаморфизм дает структуру данных, не глядя на то, что происходит с его детьми, два катаморфизма будут составлять катаморфизм.

Помимо этого, все усложняется. Пусть дифференцируются между аргументами нормального конструктора и "рекурсивными аргументами" (которые мы будем отмечать знаком "%" ). Итак, Leaf a не имеет рекурсивных аргументов, но Branch %a %b делает. Позвольте использовать термин "рекурсивная-фиксированность" конструктора для обозначения количества рекурсивных аргументов, которые он имеет. (Я составил оба эти условия, я понятия не имею, какая у них правильная терминология, если есть одна! Будьте осторожны с их использованием в другом месте!)

Если первый катаморфизм отображает что-то в конструктор нулевой рекурсивной фиксации, все хорошо!

               a               |            b            |     cata(b.a)
===============================|=========================|================
       F a %b %c .. -> Z       |      Z -> G a b ..      |      True

Если мы сопоставляем детей непосредственно с новым конструктором, мы тоже хороши.

               a               |            b            |     cata(b.a)
===============================|=========================|=================
   F a %b %c .. -> H %c %d ..  |   H %a %b -> G a b ..   |       True

Если отобразить в рекурсивную фиксацию один конструктор...

               a               |            b            |     cata(b.a)
===============================|=========================|=================
 F a %b %c .. -> A (f %b %c..) |     A %a -> B (g %a)    |    Implied by g
                               |                         | distributes over f

Но это не так. Например, если существуют g1 g2 такие, что g (f a b..) = f (g1 a) (g2 b) .., это также работает.

Отсюда, как я ожидаю, правила будут просто беспорядочными.

Ответ 3

Катаморфизмы деконструируют структуру данных в значение результата. Таким образом, в целом, когда вы применяете катаморфизм, результат - это нечто совершенно иное, и вы не можете применить к нему еще один катаморфизм.

Например, функция, суммирующая все элементы [Int], является катаморфизмом, но результат Int. Нет способа применить к нему еще один катаморфизм.

Однако некоторые специальные катаморфизмы создают результат того же типа, что и вход. Одним из таких примеров является map f (для некоторой заданной функции f). Хотя он деконструирует исходную структуру, он также создает новый список в качестве результата. (Фактически, map f можно рассматривать как как катаморфизм, так и как anamorphism.) Итак, если у вас есть такой класс специальных катаморфизмов, вы можете составить их.

Ответ 4

Если мы рассматриваем семантическую эквивалентность, то состав двух катаморфизмов является катаморфизмом, когда первый является хиломорфизмом:

cata1 . hylo1 = cata2

Например (Haskell):

sum . map (^2) = foldl' (\x y -> x + y^2) 0