Вопросы Обобщающий Функтор
Функтор в Control.Categorical.Functor имеет следующее определение:
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)
Но допустим, я хочу иметь функтор от обычных функций до стрелок Клейсли (возможно, это глупый пример) .
Я хотел бы такой тип:
fmap :: (Monad m) => (a -> b) -> Kleisli m a b
Ну, я могу позволить r = (->)
, t = Kleisli m
получить:
fmap :: (Monad m) => (a -> b) -> Kleisli m (f a) (f b)
Но тогда что f
?! Я действительно хочу, чтобы это исчезло. Я мог бы использовать функтор Identity, позволяя f = Identity
но тогда я получаю:
fmap :: (Monad m) => (a -> b) -> Kleisli m (Identity a) (Identity b)
что потребовало бы некоторого грязного развертывания.
Затем я подумал об определении Functor следующим образом:
class (Category r, Category t) => Functor r t where
type family F r t x :: *
fmap :: r a b -> t (F r t a) (F r t b)
Это позволяет мне определить экземпляр Functor для Kleisli следующим образом (без уродливой оболочки Identity):
instance (Monad m) => Functor (->) (Kleisli m) where
type F (->) (Kleisli m) a = a
fmap f = Kleisli (return . f)
И после этого я почти уверен, что нахожусь на:
fmap :: (Monad m) => (a -> b) -> Kleisli m a b
И это хорошо.
Теперь есть одна проблема, которую я могу определить сразу, а именно, для данного r
а также t
параметры для Functor
исходное определение класса допускает несколько вариантов f
тогда как с моим определением r
а также t
определить f
, Это серьезная проблема, как если бы я определил сказать:
fmap :: (a -> b) -> (Maybe a -> Maybe b)
Я не могу тогда определить:
fmap :: (a -> b) -> ([a] -> [b])
Как и в обоих случаях, r = (->)
а также t = (->)
, Так что в настоящее время мой Functor
даже не заменяет оригинальную версию Prelude.
Итак, у меня есть несколько вопросов:
- Могу ли я изменить свое определение так
r
а такжеt
не определятьf
(как оригинальная версия)? Или это потребует семейства Injective Type (я с радостью скомпилирую голову, чтобы попробовать это, если это так) . - Могу ли я изменить свое определение так, чтобы
f
а такжеr
определяетt
а такжеf
а такжеt
определяетr
? - После выполнения вышеизложенного (или нет, если это невозможно), каковы потенциальные воздействия на вывод типа?
- Есть ли что-то плохое в моем определении класса по сравнению с оригиналом, кроме таких, как усиленная типизация?
- Существуют ли альтернативные подходы, которые все еще позволяют мне определять функтор Клейсли без использования идентификатора, в то время как "лучше" того, что я предложил (более полезная структура, лучший вывод типов и т. Д.) .
Извините, последние несколько вопросов немного расплывчаты, я понимаю, что вывод типа против общности часто является компромиссом, но я просто ищу некоторые мысли по этому поводу в данном конкретном случае.
(Этот вопрос частично вытекает из ответов на этот вопрос)
1 ответ
Ближайшее, что вы можете получить, будет
class (Category r, Category t) => Functor
(f :: *) (r :: *->*->*) (t :: *->*->*) where
type F f x :: *
fmap :: Tagged f ( r a b -> t (F f a) (F f b) )
instance Functor [()] (->) (->) where
type F [()] x = [x]
fmap = Tagged map
instance (Monad m) => Functor (Kleisli m () ()) (->) (Kleisli m) where
type F (Kleisli m () ()) x = x
fmap = Tagged $ \f -> Kleisli $ return . f