Ответ 1
Если функторы F : C → D
и G : D → E
, функторная композиция G ∘ F : C → E
является отображением объектов между категориями C
и E
, что (G ∘ F)(X) = G(F(X))
и отображение между морфизмами такое, что (G ∘ F)(f) = G(F(f))
.
Это говорит о том, что ваш экземпляр CFunctor
должен быть определен следующим образом:
instance (CFunctor f, CFunctor g, Cod f ~ Dom g) => CFunctor (Wrap g f) where
type Dom (Wrap g f) = Dom f
type Cod (Wrap g f) = Cod g
cmap f = cmap (cmap f)
Однако, составление cmap
дважды дает вам Dom f a b -> Cod g (g (f a)) (g (f b))
и cmap
в этом случае имеет тип Dom f a b -> Cod g (Wrap g f a) (Wrap g f b)
.
Мы можем получить от g (f a)
до Wrap g f
и наоборот, но так как объявление экземпляра не делает никаких предположений о структуре Cod g
, нам не повезло.
Поскольку мы знаем, что функтор является отображением между категориями, мы можем использовать тот факт, что Cod g
является Category
(на стороне Хаскелла это требует ограничения Category (Cod g)
), это дает нам несколько операций для работы с
cmap f = lift? unWrap >>> cmap (cmap f) >>> lift? Wrap
Это, однако, требует удобного оператора подъема lift?
, который поднимает функцию из категории Hask
в категорию Cod g
. Написав Cod g
как (~>)
, тип lift?
должен быть:
lift? :: (a -> b) -> (a ~> b)
lift? unWrap :: Wrap g f a ~> g (f a)
cmap (cmap f) :: g (f a) ~> g (f b)
lift? Wrap :: g (f b) ~> Wrap g f b
lift? unWrap >>> cmap (cmap f) >>> lift? Wrap :: Wrap g f a ~> Wrap g f b
Теперь для этого оператора подъема есть как минимум два варианта:
- Вы можете расширить пролив от
Category (Cod g)
доArrow (Cod g)
, и в этом случае оператор подъема станетarr
, - или, как упоминает в комментариях Sjoerd Visscher, вы можете использовать тот факт, что
Wrap
иunWrap
являются семантическиid
во время выполнения, и в этом случае использованиеunsafeCoerce
оправдано.