Зачем нам контейнеры?
(в качестве оправдания: название имитирует название зачем нужны монады?)
здесь тара (и проиндексированных те) (и hasochistic) и описания. Но контейнеры проблематично и, по моему очень небольшому опыту, труднее думать в терминах контейнеров, чем в терминах описаний. Тип неиндексированных контейнеров изоморфен Σ
- это слишком неспецифический. Описание фигур и позиций помогает, Но в
⟦_⟧ᶜ : ∀ {α β γ} -> Container α β -> Set γ -> Set (α ⊔ β ⊔ γ)
⟦ Sh ◃ Pos ⟧ᶜ A = ∃ λ sh -> Pos sh -> A
Kᶜ : ∀ {α β} -> Set α -> Container α β
Kᶜ A = A ◃ const (Lift ⊥)
мы, по сути, используя Σ
вместо фигур и позиций.
тип строго положительных свободных монад над контейнерами имеет довольно простое определение, но тип Freer
монады выглядят проще для меня (и в некотором смысле Freer
монады даже лучше, чем обычно Free
монады, как описано в статьи).
Итак, что мы можем сделать с контейнерами лучше, чем с описаниями или чем-то еще?
1 ответов
на мой взгляд, ценность контейнеров (как в теории контейнеров) - это их равномерность. Это единообразие дает значительный простор для использования контейнерных представлений в качестве основы для исполняемых спецификаций и, возможно, даже машинного вывода программ.
контейнеры: теоретический инструмент, а не хорошая стратегия представления данных во время выполнения
я не рекомендую fixpoints из (нормированные) контейнеры как хорошие цель способ реализации рекурсивных структур данных. То есть полезно знать, что данный функтор имеет (до iso) представление в качестве контейнера, потому что он говорит вам, что общая функциональность контейнера (которая легко реализуется, раз навсегда, благодаря единообразию) может быть создана для вашего конкретного функтора и какого поведения вы должны ожидать. Но это не означает, что реализация контейнера будет эффективной каким-либо практическим образом. Вообще-то я предпочитаю первый заказ. кодировки (теги и кортежи, а не функции) данных первого порядка.
чтобы исправить терминологию, скажем, что тип Cont
контейнеров (on Set
, но доступны и другие категории) задается конструктором <|
упаковка из двух полей, форм и позиций
S : Set
P : S -> Set
(это та же сигнатура данных, которая используется для определения типа Сигмы, или типа Pi, или типа W, но это не означает, что контейнеры такие же, как любой из этих вещей, или что все это одно и то же.)
интерпретация такой вещи, как функтор, дается
[_]C : Cont -> Set -> Set
[ S <| P ]C X = Sg S \ s -> P s -> X -- I'd prefer (s : S) * (P s -> X)
mapC : (C : Cont){X Y : Set} -> (X -> Y) -> [ C ]C X -> [ C ]C Y
mapC (S <| P) f (s , k) = (s , f o k) -- o is composition
и мы уже побеждаем. Это map
реализовано раз и навсегда. Более того, законы функтора выполняются только вычислениями. Нет необходимости в рекурсии по структуре типов для построения операции или для доказательства законов.
описания ненормированные контейнеров
никто не пытается утверждайте, что в оперативном плане Nat <| Fin
дает эффективное реализация списков, просто, сделав эту идентификацию, мы узнаем что-то полезное о структуре списков.
позвольте мне сказать кое-что о описания. Для удобства ленивых читателей позвольте мне реконструировать их.
data Desc : Set1 where
var : Desc
sg pi : (A : Set)(D : A -> Desc) -> Desc
one : Desc -- could be Pi with A = Zero
_*_ : Desc -> Desc -> Desc -- could be Pi with A = Bool
con : Set -> Desc -- constant descriptions as singleton tuples
con A = sg A \ _ -> one
_+_ : Desc -> Desc -> Desc -- disjoint sums by pairing with a tag
S + T = sg Two \ { true -> S ; false -> T }
значения Desc
опишите функторы, точки фиксации которых дают типы данных. Какие функторы они описывают?
[_]D : Desc -> Set -> Set
[ var ]D X = X
[ sg A D ]D X = Sg A \ a -> [ D a ]D X
[ pi A D ]D X = (a : A) -> [ D a ]D X
[ one ]D X = One
[ D * D' ]D X = Sg ([ D ]D X) \ _ -> [ D' ]D X
mapD : (D : Desc){X Y : Set} -> (X -> Y) -> [ D ]D X -> [ D ]D Y
mapD var f x = f x
mapD (sg A D) f (a , d) = (a , mapD (D a) f d)
mapD (pi A D) f g = \ a -> mapD (D a) f (g a)
mapD one f <> = <>
mapD (D * D') f (d , d') = (mapD D f d , mapD D' f d')
мы неизбежно приходится работать рекурсией над описаниями, поэтому это сложнее. Законы функтора тоже не приходят бесплатно. Мы получаем лучшее представление данных, операционно, потому что нам не нужно прибегать к функциональным кодировкам, когда конкретные Кортежи будут делать. Но мы должны больше работать, чтобы писать программы.
обратите внимание, что каждый контейнер имеет описание:
sg S \ s -> pi (P s) \ _ -> var
но это также правда, что каждое описание имеет презентация как изоморфный контейнер.
ShD : Desc -> Set
ShD D = [ D ]D One
PosD : (D : Desc) -> ShD D -> Set
PosD var <> = One
PosD (sg A D) (a , d) = PosD (D a) d
PosD (pi A D) f = Sg A \ a -> PosD (D a) (f a)
PosD one <> = Zero
PosD (D * D') (d , d') = PosD D d + PosD D' d'
ContD : Desc -> Cont
ContD D = ShD D <| PosD D
то есть контейнеры-это нормальная форма для описания. Это упражнение, чтобы показать, что [ D ]D X
естественно изоморфна [ ContD D ]C X
. Это облегчает жизнь, потому что, чтобы сказать, что делать для описаний, достаточно в принципе сказать, что делать для их нормальных форм, контейнеров. Выше mapD
операция может быть, в принципе, получена путем сплавления изоморфизмов к равномерному определению mapC
.
дифференциальная структура: контейнеры показывают путь
аналогично, если у нас есть понятие равенства, мы можем сказать, что одно отверстие контексты для контейнеров единообразно
_-[_] : (X : Set) -> X -> Set
X -[ x ] = Sg X \ x' -> (x == x') -> Zero
dC : Cont -> Cont
dC (S <| P) = (Sg S P) <| (\ { (s , p) -> P s -[ p ] })
то есть форма контекста с одним отверстием в контейнере-это пара формы исходного контейнера и положения отверстия; положения являются исходными положениями, отличными от положения отверстия. Это доказательная версия "умножьте на индекс, уменьшите индекс" при дифференцировании степенных рядов.
эта равномерная обработка дает нам спецификацию, из которой мы можем вывести многовековую программу для вычисления производной полинома.
dD : Desc -> Desc
dD var = one
dD (sg A D) = sg A \ a -> dD (D a)
dD (pi A D) = sg A \ a -> (pi (A -[ a ]) \ { (a' , _) -> D a' }) * dD (D a)
dD one = con Zero
dD (D * D') = (dD D * D') + (D * dD D')
как я могу проверить правильность моего производного оператора для описаний? Сверяя его с производным от контейнеров!
не попасть в ловушку думать, что только потому, что презентация некоторых идея не может быть полезной в практическом плане, потому что она не может быть концептуально полезной.
на" свободных " монадах
одна последняя вещь. The Freer
трюк сводится к перестановке произвольного функтора определенным образом (переключение на Haskell)
data Obfuncscate f x where
(:<) :: forall p. f p -> (p -> x) -> Obfuncscate f x
но это не альтернатива для контейнеров. Это небольшое каррирование презентации контейнера. Если бы мы ... --52-->сильный экзистенциалы и зависимые типы, мы могли бы пиши
data Obfuncscate f x where
(:<) :: pi (s :: exists p. f p) -> (fst s -> x) -> Obfuncscate f x
, так что (exists p. f p)
представляет фигуры (где вы можете выбрать свое представление позиций, а затем пометить каждое место своей позицией) и fst
выбирает экзистенциального свидетеля из фигуры (представление позиции, которое вы выбрали). Он имеет достоинство быть, очевидно, строго положительным ровно потому что это презентация контейнера.
в Haskell, конечно, вы должны карри из экзистенциального, который, к счастью, оставляет зависимость только от проекции типа. Это слабость экзистенциального, которая оправдывает эквивалентность Obfuncscate f
и f
. Если вы попробуете тот же трюк в теории зависимых типов с сильными экзистенциалами, кодировка потеряет свою уникальность, потому что вы можете проецировать и различать разные варианты представления для позиций. То есть, я могу представлять Just 3
by
Just () :< const 3
или
Just True :< \ b -> if b then 3 else 5
и в Coq, скажем, это доказуемо отчетливый.
задача: характеристика полиморфных функций
каждая полиморфная функция между типами контейнеров задается определенным образом. Это единообразие снова помогает прояснить наше понимание.
если у вас есть какой-нибудь
f : {X : Set} -> [ S <| T ]C X -> [ S' <| T' ]C X
это (расширительно) дано следующими данными, которые не упоминают никаких элементов вообще:
toS : S -> S'
fromP : (s : S) -> P' (toS s) -> P s
f (s , k) = (toS s , k o fromP s)
то есть, единственный способ определить полиморфную функцию между контейнерами нужно сказать, как перевести входные фигуры в выходные фигуры, а затем сказать, как заполнить выходные позиции из входных позиций.
для предпочтительного представления строго положительных функторов дайте аналогичную жесткую характеристику полиморфных функций, которая исключает абстракцию по типу элемента. (Для описания, я буду использовать именно их reducability к контейнерам.)
задача: захват "transposability"
дали два функтора,f
и g
, легко сказать, какой их состав f o g
- это: (f o g) x
оборачивает вещи в f (g x)
, давая нам "f
-структуры g
-структур". Но можете ли вы легко наложить дополнительное условие, что все g
структуры, хранящиеся в f
структура имеет такую же форму?
допустим, что f >< g
отражает мобильные фрагмент f o g
, где g
формы такие же, так что мы с таким же успехом можно превратить вещь в g
структура f
-структур. Е. Г., в то время как [] o []
дает ободранный списки списков, [] >< []
дает прямоугольные матрицы; [] >< Maybe
дает списки, которые либо все Nothing
или Just
.
дать ><
для вашего предпочтительного представления строго положительных функторов. Для контейнеров это просто.
(S <| P) >< (S' <| P') = (S * S') <| \ { (s , s') -> P s * P' s' }
вывод
контейнеры, в их нормализованная Сигма-то-пи форма, не предназначены для эффективного машинного представления данных. Но знание того, что данный функтор, реализованный, однако, имеет представление в качестве контейнера, помогает нам понять его структуру и дать ему полезное оборудование. Многие полезные конструкции могут быть даны абстрактно для контейнеров, раз навсегда, когда они должны быть даны в каждом конкретном случае для других презентаций. Итак, да, это хорошая идея узнать о контейнерах, хотя бы для того, чтобы понять обоснование более конкретные конструкции вы фактически реализуете.