Является ли понятие "чередующегося гомоморфизма" реальным?

Мне нужен следующий класс функций:

class InterleavedHomomorphic x where
  interleaveHomomorphism :: (forall a . f a -> g a) -> x f -> x g

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


Цель этой функции состояла бы в том, что у меня есть некоторый контекст f, который аннотирует некоторые данные (Foo и Bar - это просто случайные структуры данных примера для этого вопроса):

data Foo f = One (f (Bar f)) | Product (f (Foo f)) (f (Foo f))
data Bar f = Zero | Succ (f (Bar f))

Я хотел бы преобразовать контекст данных полиморфным образом; только зная гомоморфизм между контекстами, а не (обязательно) заботясь о самих данных. Это можно сделать, предоставив instance InterleavedHomomorphic Foo и instance InterleavedHomomorphic Bar в приведенном выше примере.

Ответы

Ответ 1

Итак, если f и g являются правильными функторами, forall a. f a -> g a является естественным преобразованием. Мы могли бы сделать это немного красивее:

type f ~> g = forall a. f a -> g a

Естественные преобразования, подобные этому, образуют категорию Haskell Functors, так что у вас есть функтор от этого к какой-либо другой категории.

Следуя шагам нормальных Haskell Functors, возможно, имеет смысл иметь x быть endofunctor, отображая Функторы для других Функторов. Это похоже, но не идентично тому, что у вас есть:

class FFunctor x where
  ffmap :: (f ~> g) -> (x f ~> x g)

Однако в вашем случае x f и x g не являются функторами, а x f -> x g является нормальной функцией, а не естественным преобразованием. Тем не менее, шаблон достаточно близок, чтобы быть интригующим.

Имея это в виду, кажется, что x все еще является примером функтора, только между двумя разными категориями. Он относится к категории Функторов к категории x с различными структурами. Каждый возможный x, например Foo, формирует категорию с объектами типа Foo [] и Foo Maybe и преобразования между ними (Foo [] -> Foo Maybe). Ваша функция interleaveHomomorphism "поднимет" естественные преобразования в эти x-morphisms, точно так же, как fmap "поднимет" нормальные (a -> b) функции в функции на изображении функтора (f a -> f b).

Итак, да: ваш typeclass - это функтор, как Functor, за исключением двух разных категорий. Я не знаю конкретного имени для него, в основном потому, что я не знаю конкретного имени для конструкций вроде x.

В более общем плане, я даже не уверен, что конкретное имя имеет смысл. На данный момент нам, вероятно, понравится классный класс-класс, который будет проходить между любыми двумя категориями. Может быть, что-то вроде:

class (Category catA, Category catB) => GFunctor f catA catB where
  gfmap :: catA a b -> catB (f a) (f b)

Возможно, это уже существует в библиотеке.

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

Так что, наверное, проще всего назвать это XFunctor или что-то еще. Кроме того, представьте себе потенциал каста!

EDIT: похоже, что categories предоставляет тип CFunctor, но немного умнее:

class (Category r, Category s) => CFunctor f r s | f r -> s, f s -> r where
  cmap :: r a b -> s (f a) (f b)

Однако я не уверен, что даже это достаточно общее! Я думаю, что мы могли бы хотеть, чтобы он был более полиморфным по видам.

Ответ 2

Bar f выглядит как Free Monad Free f ().

Тогда Foo является do с одним или двумя <-. Может продолжаться оттуда...

Ответ 3

Для чего это стоит, вы можете перефразировать упрощенную версию вашего примера как

data Bar' r = Zero | Succ r
type Bar f = fix (Bar' . f)

Для каждой пары естественных преобразований eta1 :: f ~> g и eta2 :: Bar' ~> h получаем естественное преобразование (eta2 . eta1) :: (Bar' . f) ~> (h . g). И мы можем поднять это естественное преобразование по неподвижной точке очевидным образом, чтобы получить fixed (eta2 . eta1) :: Bar f -> fix (h . g). Таким образом, ваш "чередующийся гомоморфизм" является лишь частным случаем этой конструкции, когда мы имеем eta2 = id.

В целом это довольно стандартная конструкция (особенно в тех случаях, когда f является монадой или комонадой), хотя я не уверен, имеет ли это особое имя, которое широко признано.