Можно ли отменить обходы по типу?

Здесь много настроек. Если вы когда-либо видели последовательность, выровненную по типу, вы можете просмотреть все до строки.

Выровненная по типам последовательность — это все, что отдаленно похоже на

data ConsList c x y where
  CNil :: ConsList c x x     
  Cons :: c x y -> ConsList c y z -> ConsList c x z

Дан класс для индексированных функторов в стиле Атки и аппликативных функторов.

class IxFunctor f where
  ixmap :: (a -> b) -> f x y a -> f x y b
class IxFunctor f => IxApply f where
  ixap :: f i j (a -> b) -> f j k a -> f i k b
class IxApply f => IxApplicative f where
  ixpure :: a -> f i i a

и класс функторов с двумя индексами в стиле Макбрайда:

type (f :: q -> r -> *) ~~> (g :: q -> r -> *) =
  forall (i :: q) (j :: r) . f i j -> g i j

class TFunctor t where
  tmap :: (c ~~> d) -> (t c ~~> t d)

можно описать карты, складки и обходы, которые будут работать для последовательностей, выровненных по типу:

class TFoldable t where
  tfoldMap :: Category d => (c ~~> d) -> t c ~~> d
  tfoldMap f = tfoldr (\cij djk -> f cij >>> djk) id

class (TFunctor t, TFoldable t) => TTraversable t where
  ttraverse :: IxApplicative f
            => (forall x y . c x y -> f x y (d x y))
            -> (t c p q -> f p q (t d p q))

Теперь оказывается, что можно определить версию Data.Functor.Reverse для последовательностей, выровненных по типу. Конкретно

newtype Reverse t c x y = Reverse {unReverse :: t (Dual c) y x}

куда

newtype Dual c x y = Dual {getDual :: c y x}

Когда тип t на самом деле представляет собой последовательность, выровненную по типу, легко дать Reverse t полный набор операций с последовательностью, выровненной по типу.


Собственно вопрос

Что мне неясно, так это достаточно ли t быть IxTraversable для реализации IxTraversable (Reverse t). Все, что я пробовал, упиралось в кирпичную стену. Для стандартных Traversable это можно сделать с помощью аппликатива Backwards. Доступен IxBackwards, но, похоже, он не помогает. Для стандартных Traversables можно выгрузить содержимое контейнера в список, а затем вернуться из списка. Но это кажется невозможным здесь, потому что нет очевидного способа записать типы на выходе и гарантировать, что они совпадут на входе. Я что-то пропустил, или это невозможно?

Самое банальное начало:

instance IxTraversable t => IxTraversable (Reverse t) where
  ttraverse f (Reverse xs) = Reverse `ixmap` _ xs

Это дает мне дыру типа

t (Dual c) q p -> f p q (t (Dual d) q p)

Очевидная проблема заключается в том, что у нас есть t _ q p, но f p q _. Так что, если есть способ сделать это, нам, вероятно, нужно каким-то образом придумать f, который изменит ситуацию. Как я уже говорил, есть IxBackwards

newtype IxBackwards f y x a = IxBackwards { ixforwards :: f x y a }

но я не вижу, как это помогает.


person dfeuer    schedule 25.08.2016    source источник


Ответы (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.


Я попытаюсь дать пошаговый вывод, используя Typed Holes.

Начните с этого скелета, который, как мне кажется, легко вывести:

  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

Так что добавляйте больше отверстий, пока он не скомпилируется. Мой первый порыв — добавить еще одну дыру впереди (поскольку ошибка типа относится ко всему выражению, нужно что-то сделать со всем выражением, чтобы проверить тип):

  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 оба просто newtypes).

person user2407038    schedule 25.08.2016
comment
Арх! Я был очень близок к этому ответу в своих усилиях, но так и не дошел до него. Как вы думаете, вы могли бы объяснить, как вы это сделали? Или как это могло решить? - person dfeuer; 25.08.2016
comment
Я добавил подробный вывод, который, надеюсь, имеет смысл. Это по-прежнему требует некоторой проверки типов в вашей голове, но типизированные дыры делают это немного проще. - person user2407038; 25.08.2016