Ответ 1
Ваша установка звучит, и IxBackwards
полезен (на самом деле, критический) - проблема, с которой вы столкнулись, заключается в том, что обе Dual
и Reverse
заменяют позиции переменных типа, тогда как первый аргумент ttraverse
требует, чтобы позиции квантифицированных переменных типа (x,y
) в некотором смысле "соглашались". Вы должны одновременно "перевернуть" эти переменные в рекурсивном вызове на ttraverse
, используя как Dual
, так и IxBackwards
:
instance TTraversable t => TTraversable (Reverse t) where
ttraverse f =
ixmap Reverse . ixforwards .
ttraverse (IxBackwards . ixmap Dual . f . getDual) .
unReverse
Запись экземпляров для TFunctor
и TFoldable
может дать нам подсказку о том, как найти эту реализацию:
dualNat2 :: (c ~~> d) -> Dual c ~~> Dual d
dualNat2 k (Dual x) = Dual $ k x
instance TFunctor f => TFunctor (Reverse f) where
tmap f (Reverse q) = Reverse $ tmap (dualNat2 f) q
instance TFoldable f => TFoldable (Reverse f) where
tfoldMap k (Reverse z) = getDual $ tfoldMap (dualNat2 k) z
Вы по существу делаете то же самое в случае TTraversable
, за исключением того, что теперь есть два индекса для перевода:
f :: forall x y . c x y -> f x y (d x y)
ixmap Dual . f . getDual :: forall x y . Dual c y x -> f x y (Dual d y x)
IxBackwards . f :: forall x y . c x y -> IxBackwards f y x (d x y)
IxBackwards . ixmap Dual . f . getDual :: forall x y . Dual c y x -> IxBackwards f y x (Dual d y x)
и обратите внимание, что если вы только переверните один из индексов, тип функции даже не соответствует типу аргумента ttraverse
.
Я попытаюсь дать пошаговый вывод с помощью Типированных отверстий.
Начните с этого скелета, который я считаю тривиальным для вывода:
ttraverse f =
ixmap Reverse .
ttraverse _trfun .
unReverse
Это дает ошибку типа:
Couldn't match type `q' with `p'
...
Expected type: Reverse t c p q -> f p q (Reverse t d p q)
Actual type: Reverse t c q p -> f p q (Reverse t d q p)
* In the expression: ixmap Reverse . ttraverse _trfun . unReverse
Итак, добавьте больше отверстий, пока они не скомпилируются. Мой первый инстинкт - добавить еще одну дыру в фронт (так как ошибка типа для всего выражения, что-то нужно сделать для всего выражения, чтобы сделать его typecheck):
ttraverse f =
_out . ixmap Reverse .
ttraverse _trfun .
unReverse
Теперь мы не получаем ошибок типа (игнорируя ошибки "неоднозначного типа" формы C x
, где C
- класс, - ошибочны), а тип, о котором сообщается,
_out :: f0 q p (Reverse t c0 p q) -> f p q (Reverse t d p q)
где f0, c0
(в настоящее время) свободные переменные типа, которые мы используем в наших интересах! Если мы дадим c0 ~ d
и f0 ~ IxBackwards f
, то это как раз тип ixforwards
- так что попробуйте:
ttraverse f =
ixforwards . ixmap Reverse .
ttraverse _trfun .
unReverse
и теперь мы получаем хороший мономорфный предполагаемый тип:
_trfun :: Dual c x y -> IxBackwards f x y (Dual d x y)
* Relevant bindings include
f :: forall (x :: r) (y :: r). c x y -> f x y (d x y)
Теперь я также предполагаю, что очевидно, что _trfun
должно каким-то образом использовать f
, поэтому попробуйте это. Мы замечаем, что как область, так и область f
не являются точно такими, как _trfun
, поэтому мы размещаем отверстие с обеих сторон:
ttraverse f =
ixforwards . ixmap Reverse .
ttraverse (_out . f . _in) .
unReverse
и получим:
_out :: f x0 y0 (d x0 y0) -> IxBackwards f x y (Dual d x y)
_in :: Dual c x y -> c x0 y0
где x0, y0
- свободные переменные. Вероятно, наиболее очевидно, что с x0 ~ y, y0 ~ x
мы имеем _in = getDual
, поэтому мы пытаемся это сделать и получаем новый вывод:
_out :: f y x (d y x) -> IxBackwards f x y (Dual d x y)
и теперь очевидно, что переменные типа "перевернуты" в двух разных местах; один раз IxBackwards
и один раз Dual
. Способ перевернуть первую пару индексов наиболее очевидно (возможно):
ttraverse f =
ixforwards . ixmap Reverse .
ttraverse (_out . IxBackwards . f . getDual) .
unReverse
и получим:
_out :: IxBackwards f x y (d y x) -> IxBackwards f x y (Dual d x y)
Теперь у нас есть что-то вроде q A -> q B
с IxFunctor q
, поэтому, устанавливая _out = ixmap _out
, получаем
_out :: d y x -> Dual d x y
и существует тривиальная функция этого типа - а именно Dual
- которая завершает определение:
ttraverse f =
ixforwards . ixmap Reverse .
ttraverse (ixmap Dual . IxBackwards . f . getDual) .
unReverse
Обратите внимание, что некоторые композиции функций перевернуты по сравнению с исходной версией - я притворялся, что не знаю ответа априори и вывел его "самым очевидным" способом, заполнив самые простые вещи. Два определения эквивалентны (поистине эквивалентны, так как Dual
и IxBackwards
являются как раз newtype
s).