Coq описывает эти ситуации с богатым языком «комбинаторов отношений», который является лучшей версией вашей последней идеи. Я переведу это. У вас есть
ZTy : Type -- as yours
А вы приступаете к определению отношений и функций по отношениям:
-- if r : Relation t and x, y : t, we say x and y are related by r iff r x y is inhabited
Relation : Type -> Type
Relation t = t -> t -> Type
-- if x, y : ZTy, we say x and y are (Equiv)alent iff Equiv x y is inhabited, etc.
Equiv : Relation ZTy -- as yours
(=) : Relation a -- standard
Iso : Relation Type -- standard
-- f and f' are related by a ==> r if arguments related by a end up related by r
(==>) : Relation a -> Relation b -> Relation (a -> b)
(==>) xr fxr = \f, f' => (x x' : a) -> xr x x' -> fxr (f x) (f' x')
infixr 10 ==>
Идея состоит в том, что Equiv
, (=)
и Iso
- все отношения равенства. Equiv
и (=)
- два разных понятия равенства на ZTy
, а (=)
и Iso
- два понятия равенства на Type
. (==>)
объединяет отношения в новые отношения.
Если у вас есть
P : ZTy -> Type
Вы хотели бы сказать, что Equiv
аргументы alent отображаются на Iso
морфических типов. То есть вам нужно
replaceP : (x x' : ZTy) -> Equiv x x' -> Iso (P x) (P x')
Как может помочь язык отношений? Ну, по сути, replaceP
говорит о том, что P
"равно" самому себе в соответствии с отношением Equiv ==> Iso
(NB Equiv ==> Iso
не является эквивалентностью, но единственное, чего ему не хватает, это рефлексивности.) Если функция не «равно» себе в Equiv ==> Iso
, тогда это немного похоже на «противоречие», и эта функция «не существует» в вашей вселенной. Или, скорее, если вы хотите написать функцию
f : (ZTy -> Type) -> ?whatever
Вы можете ограничить себя правильным типом функций, требуя аргумент доказательства, например, так:
Proper : Relation a -> a -> Type
Proper r x = r x x
f : (P : ZTy -> Type) -> Proper (Equiv ==> Iso) P -> ?whatever
Обычно, однако, вы отбрасываете доказательства, если в этом нет крайней необходимости. На самом деле стандартная библиотека уже содержит множество функций на ZTy
, например
concatMap : Monoid m => (ZTy -> m) -> List ZTy -> m
Вместо того, чтобы писать concatMap
с аргументом доказательства, вам действительно нужно только написать доказательство о concatMap
:
concatMapProper : Proper ((Equiv ==> (=)) ==> Pairwise Equiv ==> (=))
-- you'd really abstract over Equiv and (=), but then you need classes for Relations
Pairwise : Relation a -> Relation [a] -- as you may guess
Я не уверен, какой принцип индукции вы хотите написать, поэтому я просто оставлю это в покое. Тем не менее, вы обеспокоены тем, что
proof : Property Constant
всегда необходимо заменить на
proof : (k : ZTy) -> Equiv k Constant -> Property k
является лишь частично обоснованным. Если у вас уже есть
PropertyProper : Proper (Equiv ==> Iso) Property
, что вы, скорее всего, должны, тогда вы можете написать proper : Property Constant
и просто протолкнуть его через PropertyProper
, чтобы обобщить его при необходимости. (Или напишите proper
с общей подписью, используя простое определение с PropertyProper
сверху). Вы не можете отказаться от написания доказательства где-то , потому что оно просто не так тривиально.
Стоит также отметить, что (==>)
использует иное, чем в качестве аргумента Proper
. Он служит универсальным расширением равенства:
abs1 : ZTy -> Nat
abs1 (MkZ l r) = go l r
where go (S n) (S m) = go n m
go Z m = m
go n Z = n
abs2 : ZTy -> Nat
abs2 (MkZ l r) = max l r - min l r
absEq : (Equiv ==> (=)) abs1 abs2
-- : (x x' : ZTy) -> Equiv x x' -> abs1 x = abs2 x'