Можно ли обобщить этот lmap

Я хотел бы немного обобщить бифунктор lmap.

lmap обычно принимает функцию и отображает ее на левый функтор в бифункторе.

Для начала я обобщу идею Functor на категории за пределами (->) (это поможет нам избавиться от необходимости в классе Bifunctor).

class Category cat where
  id  :: cat a a
  (.) :: cat b c -> cat a b -> cat a c

instance Category (->) where
  id x = x
  (f . g) a = f (g a)

class (Category s, Category t) => Functor s t f where
  map :: s a b -> t (f a) (f b)

Мне также понадобится Flip, чтобы я мог создавать контравариантные функторы и бифункторы.

newtype Flip p a b =
  Flip
   { unflip :: p b a
   }

Теперь я могу написать свой lmap, подняв обычный map на Flip:

lmap c = unflip . map c . Flip

Это переворачивает бифунктор, применяет карту, а затем переворачивает ее обратно. Однако теперь возникает проблема, что Flip и unflip имеют довольно ограниченные типы.

Flip   :: p b a -> Flip p a b
unflip :: Flip p a b -> p b a

Это означает, что когда я получу тип

lmap ::
  ( Functor s (->) (Flip p c)
  )
    => s a b -> p a c -> p b c

Здесь (->) в Flip и unflip заставляет наши функторы отображаться в категорию (->).

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

Flip :: Flip (->) (p a b) (Flip p b a)
Flip :: Monad m => Kleisli m (p a b) (Flip p b a)
Flip :: Monad m => Flip (Kleisli m) (p a b) (Flip p b a)

и так далее. Фактически, для каждого экземпляра Category, который я могу придумать, есть ясный любой простой экземпляр Flip. Но я явно не могу построить Flip из (.) и id в одиночку.

Таким образом, я действительно хотел бы обобщить lmap на

lmap ::
  ( Functor s t (Flip p c)
  )
    => s a b -> t (p a c) (p b c)

Что делает его намного больше похожим на map.

Это возможно? Есть ли способ реализовать этот тип, или я застрял на (->)?


person Éamonn Olive    schedule 04.05.2020    source источник