Функтор для (a → b) → (f a → f b), что для (Категория c) => c a b → c (f a) (f b)?
Я хотел бы иметь функцию для того, чтобы либо отображать чистую функцию в контейнер, либо последовательность ее применения/монадического действия. Для чистого отображения имеем
fmap :: Functor f => (a -> b) -> (f a -> f b)
Для монадического секвенирования мы имеем (из Data.Taversable)
mapM :: (Traversable f, Monad m) => (a -> m b) -> (f a -> m (f b))
Что похоже на
mapKleisli :: (Traversable f, Monad m) => Kleisli m a b -> Kleisli m (f a) (f b)
mapKleisli = Kleisli . mapM . runKleisli
Мы знаем, что оба (- > ) и (Kleisli m) являются категориями (и стрелками). Поэтому естественно сделать обобщение:
mapCategory :: (X f, Category c) => c a b -> c (f a) (f b)
Знаете ли вы такой класс X с похожим методом? Может быть, где-то в хаке? Я попытался hoogle/hayoo, но не нашел ничего подходящего.
Update:
Теперь я лучше знаю, что мне нужно. Стрелки Kleisli и (- > ) являются экземплярами ArrowApply, которые так же сильны, как Monad. Я придумал эту версию Travesable на основе стрелок:
{-# LANGUAGE TypeOperators #-}
import Prelude hiding (id, (.), mapM)
import Control.Arrow
import Control.Category
class Traversable f where
traverse :: ArrowApply (~>) => f a -> (a ~> b) ~> f b
mapArrow :: (ArrowApply (~>), Traversable f) => a ~> b -> f a ~> f b
mapArrow a = arr (\x -> (traverse x, a)) >>> app
instance Traversable Maybe where
traverse Nothing = arr (const Nothing)
traverse (Just x) = arr (\a -> (a, x)) >>> app >>> arr Just
instance Traversable [] where
traverse [] = arr (const [])
traverse (x : xs) = undefined -- this is hard!
Я мог бы использовать обычный обычный метод Traversable, с Identity для чистых функций, но я не уверен, что это хорошо. Рассмотрение чистых функций как особого случая монадических действий является странным. Интерпретация как чистых функций, так и монадических действий как экземпляров какого-либо класса действий (Category/Arrow/ArrowApply) выглядит более понятным для меня.
Вопросы: хотите ли вы закончить экземпляр для []
? Имеет ли мое мнение о ArrowApply против Monad какой-либо смысл?
Ответы
Ответ 1
Вы запрашиваете "некоторый класс X", но должно быть довольно ясно, что самым (или, возможно, единственным) правильным именем для этого класса будет "Functor". Вы хотите просто класс функтора, определенный для произвольного экземпляра Category
, а не только (->)
.
Конечно, ваше определение по-прежнему ограничено (endo) функторами от категории до подкатегории, определенной конструктором типа, предоставляющим экземпляр. Если вы обобщите еще немного, нет никаких причин для того, чтобы две категории были одинаковыми, предоставив класс типа что-то вроде этого:
class (Category r, Category t) => Functor f r t | f r -> t, f t -> r where
fmap :: r a b -> t (f a) (f b)
Это все еще ужасно ограничено по сравнению с полной концепцией функтора в теории категорий, но хорошо.
Интересно также заметить, что в нем все еще есть конструктор типа (->)
- потому что, хотя мы моделируем исходную и целевую категории с произвольными экземплярами, все это (и, в частности, функтор сам) по-прежнему существует в некотором смысле в Hask, т.е. в категории, связанной с (->)
. Другая половина функтора (объекты отображения части), грубо говоря, (->)
в виде * -> *
для конструктора типов f
.