Эта запись Reddit Эдварда Кметта дает конструктивное определение < strong>естественная карта, та, что из теоремы о свободе для fmap
(которую я прочитал в еще одном сообщение Эдварда Кметта):
Для заданных
f
,g
,h
иk
таких, чтоf . g = h . k
:$map f . fmap g = fmap h . $map k
, где$map
— естественная карта для данного конструктора.
Я не совсем понимаю алгоритм. Давайте подойдем к этому шаг за шагом:
Мы можем определить естественную карту с помощью индукции по любому конкретному выбору
F
, который вы даете. В конечном счете любой такой АТД состоит из сумм, произведений,(->)
,1
,0
,a
, вызовов других функторов и т. д.
Рассмотреть возможность:
data Smth a = A a a a | B a (Maybe a) | U | Z Void deriving ...
Никаких стрел. Давайте посмотрим, как fmap
(который я считаю естественным выбором для любого АТД без (->)
) будет работать здесь:
instance Functor Smth where
fmap xy (A x x1 x2) = A (xy x) (xy x1) (xy x2)
fmap xy (B x xPlus1) = B (xy x) (fmap xy xPlus1)
-- One can pattern-match 'xPlus1' as 'Just x1' and 'Nothing'.
-- From my point of view, 'fmap' suits better here. Reasons below.
fmap _xy U = U
fmap _xy (Z z) = absurd z
Что кажется естественным. Говоря более формально, мы применяем xy
к каждому x
, применяем fmap xy
к каждому T x
, где T
— это Functor
, оставляем все единицы без изменений и передаем каждое Void
в absurd
. Это работает и для рекурсивных определений!
data Lst a = Unit | Prepend a (Lst a) deriving ...
instance Functor Lst where
fmap xy Unit = Unit
fmap xy (Prepend x lstX) = Prepend (xy x) (fmap xy lstX)
А для неиндуктивных типов: (Подробное объяснение в этом ответе под связанным постом.)
Graph a = Node a [Graph a]
instance Functor Graph where
fmap xy (Node x children) = Node (xy x) (fmap (fmap xy) children)
Эта часть ясна.
Когда мы разрешаем
(->)
, у нас появляется первое, что смешивает дисперсию. Аргумент левого типа(->)
находится в контравариантной позиции, правая часть находится в ковариантной позиции. Таким образом, вам нужно отслеживать конечную переменную типа по всему АТД и смотреть, встречается ли она в положительной и/или отрицательной позиции.
Теперь включаем (->)
s. Попробуем продолжить эту индукцию:
Каким-то образом мы получили естественные карты для T a
и S a
. Таким образом, мы хотим рассмотреть следующее:
data T2S a = T2S (T a -> S a)
instance ?Class? T2S where
?map? ?? (T2S tx2sx) = T2S $ \ ty -> ???
И я считаю, что это тот момент, когда мы начинаем выбирать. У нас есть следующие варианты:
- (Phantom)
a
не встречается ни вT
, ни вS
.a
вT2S
является фантомным, поэтому мы можем реализовать какfmap
, так иcontramap
какconst phantom
. - (Ковариант)
a
встречается вS a
и не встречается вT a
. Таким образом, это что-то из строкReaderT
сS a
(которое на самом деле не зависит отa
) как окружение, которое заменяет?Class?
наFunctor
,?map?
наfmap
,???
,??
наxy
на:let tx = phantom ty sx = tx2sx tx sy = fmap xy sx in sy
- (контравариант)
a
встречается вT a
и не встречается вS a
. Я не вижу способа сделать эту штуку ковариантным функтором, поэтому мы реализуем здесь экземплярContravariant
, который заменяет?map?
наcontramap
,??
наyx
,???
на:let tx = fmap yx ty sx = tx2sx tx sy = phantom sx in sy
- (неизменяемый) a>
a
встречается как вT a
, так и вS a
. Мы больше не можем использоватьphantom
, что очень кстати. Существует модульData.Functor.Invariant
от Эдварда Кметта. . Он предоставляет следующий класс с картой:
И все же я не вижу способа превратить это во что-то, что мы могли бы включить в свободную теорему для fmap — этот тип требует дополнительной функции-аргумента, которая мы не можем отмахнуться какclass Invariant f where invmap :: (a -> b) -> (b -> a) -> f a -> f b -- and some generic stuff...
id
или что-то в этом роде. Так или иначе, мы ставимinvmap
вместо?map?
,xy yx
вместо??
и следующее вместо???
:let tx = fmap yx ty sx = tx2sx tx sy = fmap xy sx in sy
Итак, правильно ли я понимаю такой алгоритм? Если да, то как нам правильно обработать случай Invariant?
T a -> S a
в (ковариантный) функтор, чтобы определитьfmap
, вам по существу нужно, чтобыT
былContravariant
, аS
был (ковариантным)Functor
. Тогда вы получите что-то вродеfmap (f :: a->b) (myFunction :: T a -> S a) = fmap f . myFunction . contramap f :: T b -> S b
. - person chi   schedule 29.04.2021Functor
из случая Инвариант. - person Zhiltsoff Igor   schedule 29.04.2021invariant
названо неправильно. Кажется, вы последовательно предполагаете, чтоa
появляется вt
на самом деле означает, чтоa
появляется ковариантно вt
, что является неверным предположением. ТипS a -> T a
может иметьa
, появляющийся как вS a
, так и вT a
, и по-прежнему быть ковариантным, контравариантным или инвариантным. - person Daniel Wagner   schedule 29.04.2021a
находится в отрицательной позиции вT a
и фантом вS a
, он находится в отрицательном кратном отрицательном, то есть положительном, позиция вT a -> S a
и положительном кратном отрицательном, то есть отрицательном , позиция вS a -> T a
. Я правильно это объясняю? Действительно,data T a = T ((a -> Int) -> Int) deriving Functor
работает. - person Zhiltsoff Igor   schedule 29.04.2021