Вопросы Обобщающий Функтор

Функтор в 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.

Итак, у меня есть несколько вопросов:

  1. Могу ли я изменить свое определение так r а также t не определять f (как оригинальная версия)? Или это потребует семейства Injective Type (я с радостью скомпилирую голову, чтобы попробовать это, если это так) .
  2. Могу ли я изменить свое определение так, чтобы f а также r определяет t а также f а также t определяет r?
  3. После выполнения вышеизложенного (или нет, если это невозможно), каковы потенциальные воздействия на вывод типа?
  4. Есть ли что-то плохое в моем определении класса по сравнению с оригиналом, кроме таких, как усиленная типизация?
  5. Существуют ли альтернативные подходы, которые все еще позволяют мне определять функтор Клейсли без использования идентификатора, в то время как "лучше" того, что я предложил (более полезная структура, лучший вывод типов и т. Д.) .

Извините, последние несколько вопросов немного расплывчаты, я понимаю, что вывод типа против общности часто является компромиссом, но я просто ищу некоторые мысли по этому поводу в данном конкретном случае.

(Этот вопрос частично вытекает из ответов на этот вопрос)

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
Другие вопросы по тегам