Arrow
объединяет два понятия. Один из них, как вы говорите, принадлежит профунктору, но прежде всего это просто определенный класс категорий (о чем и свидетельствует суперкласс).
Это очень важно для этого вопроса: да, сигнатура fmap
— это (a -> b) -> f a -> f b
, но на самом деле это далеко не все, что может делать функтор! В математике функтор — это сопоставление двух категорий C и D, которое сопоставляет каждой стрелке в C стрелку в D. эм>. Стрелки в разных категориях, то есть! Стандартный класс Functor
просто охватывает простейший частный случай — эндофункторов в категории Hask.
Полная общая версия класса функтора на самом деле выглядит примерно так (здесь моя версия из ограниченных категорий):
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)
Или, в псевдосинтаксисе,
class (Category (──>), Category (~>)) => Functor f (──>) (~>) where
fmap :: (a ──> b) -> f a ~> f b
Это также может работать, когда одна из категорий является правильной стрелкой, а не обычной функциональной категорией. Например, вы можете определить
instance Functor Maybe (Kleisli [] (->)) (Kleisli [] (->)) where
fmap (Kleisli f) = Kleisli mf
where mf Nothing = [Nothing]
mf (Just a) = Just <$> f a
использоваться как
> runKleisli (fmap . Kleisli $ \i -> [0..i]) $ Nothing
[Nothing]
> runKleisli (fmap . Kleisli $ \i -> [0..i]) $ Just 4
[Just 0,Just 1,Just 2,Just 3,Just 4]
Не уверен, будет ли это полезно для чего-то нетривиального, если использовать стандартные профункторные стрелки. Это определенно полезно в других категориях, которые не Hask-профункторы, например
instance (TensorSpace v) => Functor (Tensor s v) (LinearFunction s) (LinearFunction s)
выражая, что вы можете отобразить линейную функцию по одному фактору тензорного произведения (тогда как обычно невозможно отобразить нелинейную функцию по такому продукту - результат будет зависеть от выбора базиса в векторном пространстве).
person
leftaroundabout
schedule
15.04.2017