Я хотел бы немного обобщить бифунктор 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
.
Это возможно? Есть ли способ реализовать этот тип, или я застрял на (->)
?