Как мне доказать, казалось бы, очевидный факт, когда соответствующие типы абстрагируются лямбдой в Идрисе?

Я пишу базовый монадический парсер в Idris, чтобы привыкнуть к синтаксису и отличиям от Haskell. У меня есть основы, которые работают нормально, но я застрял на попытке создать экземпляры VerifiedSemigroup и VerifiedMonoid для парсера.

Без лишних слов, вот тип парсера, экземпляры Semigroup и Monoid, а также начало экземпляра VerifiedSemigroup.

data ParserM a          = Parser (String -> List (a, String))
parse                   : ParserM a -> String -> List (a, String)
parse (Parser p)        = p
instance Semigroup (ParserM a) where
    p <+> q             = Parser (\s => parse p s ++ parse q s)
instance Monoid (ParserM a) where
    neutral             = Parser (const []) 
instance VerifiedSemigroup (ParserM a) where
    semigroupOpIsAssociative (Parser p) (Parser q) (Parser r) = ?whatGoesHere

Я практически застрял после intros со следующим состоянием прувера:

-Parser.whatGoesHere> intros
----------              Other goals:              ----------
{hole3},{hole2},{hole1},{hole0}
----------              Assumptions:              ----------
 a : Type
 p : String -> List (a, String)
 q : String -> List (a, String)
 r : String -> List (a, String)
----------                 Goal:                  ----------
{hole4} : Parser (\s => p s ++ q s ++ r s) =
          Parser (\s => (p s ++ q s) ++ r s)
-Parser.whatGoesHere> 

Похоже, я смогу каким-то образом использовать rewrite вместе с appendAssociative, но я не знаю, как «проникнуть внутрь» лямбда \s.

В любом случае, я застрял на части упражнения, посвященной доказательству теорем, и, похоже, я не могу найти много документации по доказательству теорем, ориентированной на Идрис. Я думаю, может быть, мне нужно начать смотреть учебники Agda (хотя Idris - это язык с зависимой типизацией, я уверен, что хочу выучить!).


person h.forrest.alexander    schedule 07.08.2014    source источник
comment
Чтобы попасть под лямбду, вам понадобится расширяемость функции (funext : (f, g : a -> b) -> ((x : a) -> f x = g x) -> f = g). К сожалению, ни Агда, ни Идрис (насколько мне известно) не могут доказать это утверждение, поэтому его следует рассматривать как аксиому. Другой вариант - ввести свое собственное понятие равенства (например, p = q <=> forall s. parse p s = parse q s), но я не думаю, что VerifiedSemigroup Идриса способен иметь дело с настраиваемым равенством.   -  person Vitus    schedule 07.08.2014
comment
Спасибо! Это несколько проясняет ситуацию. Зная терминологию, расширяемость функций приводит меня к более глубокому объяснению этой идеи. Похоже, я бы где-то полагался на аксиому, чтобы заставить этот проверенный экземпляр работать - по крайней мере, на данный момент. Так что я не совсем уверен, как действовать дальше - funext = believe me похоже на обман - опять же, мне не нужно строгое доказательство проблемы с игрушкой, которая в моих прошлых попытках на других языках могла бы иметь любые доказательства только в комментариях, если вообще.   -  person h.forrest.alexander    schedule 09.08.2014
comment
Поскольку ответов по-прежнему нет: вы хотите, чтобы я превратил комментарий в настоящий ответ?   -  person Vitus    schedule 10.08.2014
comment
У меня еще не было времени, чтобы попытаться решить проблему с помощью вашего ответа. Я планировал попытаться разобраться в этом, а затем обновить свой вопрос, но ударил по работе и финалу летнего семестра. Если у вас есть решение, я бы обязательно его увидел!   -  person h.forrest.alexander    schedule 10.08.2014


Ответы (1)


Ответ прост: вы не можете. Рассуждения о функциях довольно неудобны в теориях интенсионального типа. Например, теория типов Мартина-Лёфа не может доказать:

S x + y = S (x + y)
0   + y = y

x +′ S y = S (x + y)
x +′ 0   = x

_+_ ≡ _+′_  -- ???

(насколько мне известно, это реальная теорема, а не просто «доказательство отсутствия воображения»; однако я не смог найти источник, где я ее прочитал). Это также означает, что нет никаких доказательств для более общего:

ext : ∀ {A : Set} {B : A → Set}
        {f g : (x : A) → B x} →
        (∀ x → f x ≡ g x) → f ≡ g

Это называется расширяемостью функции: если вы можете доказать, что результаты равны для всех аргументов (то есть функции равны экстенсивно), то функции также равны.

Это отлично подойдет для вашей проблемы:

<+>-assoc : {A : Set} (p q r : ParserM A) →
  (p <+> q) <+> r ≡ p <+> (q <+> r)
<+>-assoc (Parser p) (Parser q) (Parser r) =
  cong Parser (ext λ s → ++-assoc (p s) (q s) (r s))

где ++-assoc - ваше доказательство ассоциативности свойства _++_. Я не уверен, как это будет выглядеть с точки зрения тактики, но это будет довольно похоже: примените сравнение для Parser, и цель должна быть такой:

(\s => p s ++ q s ++ r s) = (\s => (p s ++ q s) ++ r s)

Затем вы можете применить расширяемость, чтобы получить предположение s : String и цель:

p s ++ q s ++ r s = (p s ++ q s) ++ r s

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

  • Потеря согласованности (т. Е. Возможность доказать ложность; хотя я думаю, что расширяемость функции в порядке)

  • Прерывание редукции (что делает функция, которая выполняет анализ случаев только для refl, при данной аксиоме?)

Я не уверен, как Идрис трактует аксиомы, поэтому не буду вдаваться в подробности. Только помните, что аксиомы могут кое-что испортить, если вы не будете осторожны.


Сложный вариант - работать с сетоидами. Сетоид - это, по сути, тип, снабженный настраиваемым равенством. Идея состоит в том, что вместо Monoid (или VerifiedSemigroup в вашем случае), который работает со встроенным равенством (= в Идрисе, в Agda), у вас есть специальный моноид (или полугруппа) с другим базовым равенством. Обычно это делается путем упаковки операций моноида (полугруппы) вместе с равенством и кучей доказательств, а именно (в псевдокоде):

=   : A → A → Set  -- equality
_*_ : A → A → A    -- associative binary operation
1   : A            -- neutral element

=-refl  : x = x
=-trans : x = y → y = z → x = z
=-sym   : x = y → y = x

*-cong : x = y → u = v → x * u = y * v  -- the operation respects
                                        -- our equality

*-assoc : x * (y * z) = (x * y) * z
1-left  : 1 * x = x
1-right : x * 1 = x

Выбор равенства для парсеров очевиден: два парсера равны, если их выходы совпадают для всех возможных входов.

-- Parser equality
_≡p_ : {A : Set} (p q : ParserM A) → Set
Parser p ≡p Parser q = ∀ x → p x ≡ q x

Это решение имеет разные компромиссы, а именно то, что новое равенство не может полностью заменить встроенное (это обычно проявляется, когда вам нужно переписать некоторые термины). Но это здорово, если вы просто хотите показать, что ваш код делает то, что должен делать (с точностью до некоторого пользовательского равенства).

person Vitus    schedule 17.08.2014
comment
Большое спасибо за подробный ответ! Хотя мне все еще (очевидно) нужно выделить время, чтобы применить эти идеи в моем собственном коде, чтобы по-настоящему прочувствовать их, этот ответ дает мне очень хорошую отправную точку для размышлений об ограничениях теории интенсиональных типов в программировании с зависимым типом - а также как использовать аксиомы или устанавливать новые идеи равенства в сетоиде. Если бы я не начал использовать StackOverflow только для того, чтобы задать этот вопрос, то есть если бы у меня была как минимум 15 репутация, я бы тоже проголосовал за этот ответ. Еще раз спасибо! - person h.forrest.alexander; 19.08.2014
comment
@ h.forrest.alexander: Не волнуйся. Рад, что смог помочь! - person Vitus; 19.08.2014