Доказательство теорем о функциях со случаями - PullRequest
0 голосов
/ 01 мая 2018

Скажем, у нас есть функция merge, которая просто объединяет два списка:

Order : Type -> Type
Order a = a -> a -> Bool

merge : (f : Order a) -> (xs : List a) -> (ys : List a) -> List a
merge f xs [] = xs
merge f [] ys = ys
merge f (x :: xs) (y :: ys) = case x `f` y of
                                   True  => x :: merge f xs (y :: ys)
                                   False => y :: merge f (x :: xs) ys

и мы хотели бы доказать что-нибудь умное, например, что объединение двух непустых списков приводит к непустому списку:

mergePreservesNonEmpty : (f : Order a) ->
                         (xs : List a) -> (ys : List a) ->
                         {auto xsok : NonEmpty xs} -> {auto ysok : NonEmpty ys} ->
                         NonEmpty (merge f xs ys)
mergePreservesNonEmpty f (x :: xs) (y :: ys) = ?wut

Проверка типа отверстия wut дает нам

wut : NonEmpty (case f x y of   True => x :: merge f xs (y :: ys) False => y :: merge f (x :: xs) ys)

Имеет смысл до сих пор! Итак, давайте продолжим и разделим регистр, как предполагает этот тип:

mergePreservesNonEmpty f (x :: xs) (y :: ys) = case x `f` y of
                                                    True => ?wut_1
                                                    False => ?wut_2

Кажется разумным надеяться, что типы wut_1 и wut_2 будут соответствовать соответствующим ветвям выражения случая merge (поэтому wut_1 будет что-то вроде NonEmpty (x :: merge f xs (y :: ys)), что может быть мгновенно выполнено ), но наши надежды не оправдались: типы такие же, как и для оригинального wut.

Действительно, похоже, единственный способ - использовать with -класс:

mergePreservesNonEmpty f (x :: xs) (y :: ys) with (x `f` y)
  mergePreservesNonEmpty f (x :: xs) (y :: ys) | True = ?wut_1
  mergePreservesNonEmpty f (x :: xs) (y :: ys) | False = ?wut_2

В этом случае типы будут такими, как ожидалось, но это приводит к повторению аргументов функции для каждой ветви with (и все становится хуже, когда with становится вложенным), плюс with, похоже, не играет хорошо с неявными аргументами (но это, вероятно, само по себе стоит вопроса).

Итак, почему case здесь не поможет, есть ли какие-то причины, помимо чисто практической реализации, которые не соответствуют его поведению с поведением with, и есть ли другие способы написания этого доказательства?

Ответы [ 2 ]

0 голосов
/ 02 мая 2018

Есть проблема с проверкой вещей с помощью case: https://github.com/idris-lang/Idris-dev/issues/4001

Из-за этого в idris-bi нам в конечном итоге пришлось удалить все case в таких функциях и определить отдельных помощников верхнего уровня, которые соответствуют условию случая, например, как здесь .

0 голосов
/ 02 мая 2018

Вещи слева от | необходимы только в том случае, если новая информация каким-то образом распространяется обратно к аргументам.

mergePreservesNonEmpty : (f : Order a) ->
                         (xs : List a) -> (ys : List a) ->
                         {auto xsok : NonEmpty xs} -> {auto ysok : NonEmpty ys} ->
                         NonEmpty (merge f xs ys)
mergePreservesNonEmpty f (x :: xs) (y :: ys) with (x `f` y)
  | True = IsNonEmpty
  | False = IsNonEmpty

-- for contrast
sym' : (() -> x = y) -> y = x
sym' {x} {y} prf with (prf ())
-- matching against Refl needs x and y to be the same
-- now we need to write out the full form
  sym' {x} {y=x} prf | Refl = Refl

Что касается , почему это так, я верю, что это просто реализация, но тот, кто знает лучше, может оспорить это.

...