Проверка на evars в тактике, которая возвращает значение

Я пытаюсь написать тактику, которая возвращает значение, и при этом ей нужно проверить, является ли что-то эваром.

К сожалению, я не могу использовать is_evar, потому что тогда считается, что тактика возвращает не значение (а скорее другую тактику). Пример ниже.

Какие-либо предложения?

Ltac reify_wrt values ls :=
  match ls with
  | nil => constr:(@nil nat)
  | ?a :: ?ls' => let i := lookup a values in
                 let idx := reify_wrt values ls' in
                 constr:(i :: idx)
  | ?e :: ?ls' => is_evar e; 
                  let i := constr:(100) in 
                  let idx := reify_wrt values ls' in
                  constr:(i :: idx)
  end.

person Rand00    schedule 29.08.2017    source источник
comment
Я не уверен, что это лучшая идея, но некоторые опытные пользователи уже пробуют LTAC2, который может помочь в решении этой проблемы. LTAC2 поддерживается в Gitter/Github.   -  person ejgallego    schedule 30.08.2017


Ответы (2)


Существует изящный (или неприятный) маленький трюк, который вы можете использовать, чтобы вставить выполнение тактики в конструкцию конструкции в Coq >= 8.5: оберните его в match goal.

Ltac reify_wrt values ls :=
  match ls with
  | nil => constr:(@nil nat)
  | ?a :: ?ls' => let i := lookup a values in
                 let idx := reify_wrt values ls' in
                 constr:(i :: idx)
  | ?e :: ?ls' => let check := match goal with _ => is_evar e end in
                  let i := constr:(100) in 
                  let idx := reify_wrt values ls' in
                  constr:(i :: idx)
  end.

Поскольку меня завораживают тайны языков программирования, я расскажу вам больше, чем вы, вероятно, когда-либо хотели узнать о настоящих и прошлых моделях выполнения Ltac.

Есть две фазы тактической оценки: тактическая оценка экспрессии и тактический бег. Во время выполнения тактики выполняется упорядочивание, уточнение, перезапись и т. д. Во время оценки тактического выражения оцениваются следующие конструкции, если они находятся в позиции головы тактического выражения:

  • тактические призывы разрешены/отслеживаются/встраиваются/разворачиваются
  • let ... in ... связывает оценку выражения своего аргумента с именем и выполняет замену
  • constr:(...) оценивается и проверяется тип
  • lazymaytch ... with ... end оценивается (с обратным отслеживанием) и возвращает первую совпадающую ветвь, которая успешно оценивает выражение
  • match ... with ... end оценивается (с возвратом) и ветвь с готовностью выполняется. Обратите внимание, что на этой картинке match выглядит странно, потому что заставляет выполнять тактику раньше. Если вы когда-либо видели «тактику немедленного создания матчей, не разрешенную в локальных определениях» в Coq ‹ 8.5, это сообщение об ошибке явно запрещает поведение, которое я использую выше. Я предполагаю, что это потому, что это странное поведение match является бородавкой, которую оригинальные разработчики, реализующие Ltac, хотели скрыть. Таким образом, единственное место, где вы можете заметить это в Coq 8.4, это если вы вставите match внутри lazymatch и поиграете с уровнями отказов, и заметите, что lazymatch будет возвращаться назад при неудачах выполнения тактики во внутреннем match, когда вы обычно ожидаете, что он потерпит неудачу.

В Coq 8.5 тактический движок был переписан для обработки зависимых подцелей. Это вызвало тонкие изменения в семантике ;, которые можно наблюдать только при использовании evars, которые являются общими для нескольких целей. В переписывании разработчики изменили семантику lazymatch на «match без возврата» и сняли ограничение на «тактику немедленного создания матчей». Следовательно, вы можете делать странные вещи, например:

let dummy := match goal with _ => rewrite H end in
constr:(true)

и иметь тактику создания конструкций с побочными эффектами. Однако вы больше не можете делать:

let tac := lazymatch b with
                | true => tac1
                | false => tac2
                end in
tac long args.

потому что в Coq >= 8.5 lazymatch также жадно оценивает свои ветки.

person Jason Gross    schedule 12.09.2017

Это известное ограничение Ltac: нельзя написать тактику, которая иногда возвращает значение, а иногда возвращает другую тактику. Решение состоит в том, чтобы переписать свою тактику в стиле продолжения передачи. К сожалению, я не могу дать вам подробное объяснение, как это сделать, но CDPT Адама Члипалы имеет глава Ltac, описывающая проблему; просто ищите "продолжение" в тексте.

person Arthur Azevedo De Amorim    schedule 30.08.2017
comment
Обратите внимание, что приведенный выше код всегда должен возвращать значение (как и тот же код с tryif is_evar a then b else c, который также не работает), но похоже, что уже отмеченный CPDT, который возвращает значение, очень узко определен для Ltac. - person Rand00; 30.08.2017
comment
Во всяком случае, мне удалось написать аналогичную функцию (которая просто удаляет эвары), используя метод CPDT. Ltac rm_evar ls cont := match ls with | nil => cont ls | ?a :: ?ls' => is_evar a; rm_evar ls' ltac:(fun l => cont l) | ?a :: ?ls' => rm_evar ls' ltac:(fun l => cont (a :: l)) end. Любые мысли о последующем извлечении значения? (CPDT вызовет это с помощью rm_evar ls1 ltac:(fun n => pose n) — думаю, я мог бы попытаться сопоставить гипотезу в остальной части ltac, но это не идеально.) - person Rand00; 30.08.2017
comment
Если я понимаю, что вы пытаетесь сделать, вам нужно поместить любой код, использующий значение, в продолжение верхнего уровня. Что-то вроде rm_evar ls1 ltac:(fun n => do_something_with_n n). - person Arthur Azevedo De Amorim; 31.08.2017
comment
Нет, этот ответ неверен. Это хорошо известное поверхностное ограничение в Coq 8.4 и более ранних версиях. Даже в Coq 8.4 вы можете обойти это с помощью классов типов, если тактика, которую вам нужно вызвать, не является тактикой более высокого порядка, то есть вам не нужно передавать ей другую тактику. Но в Coq 8.5 и более поздних версиях особых проблем нет; см. ответ, который я только что опубликовал на stackoverflow.com/a/46178884/377022 - person Jason Gross; 12.09.2017
comment
Чтобы быть более точным, этот ответ технически верен в Coq 8.5 и более поздних версиях (отчасти потому, что каждая тактика, которая, кажется, не возвращает значение при запуске, на самом деле возвращает единицу значения), не отвечая на вопрос (задающий вопрос хочет тактику который всегда возвращает значение). Это неправильно в Coq 8.4 и более ранних версиях, о чем свидетельствует тактика lazymatch goal with |- True => constructor | |- ?G => constr:(G) end, которая решит цель True, если вы ее запустите, и вернет значение цели в противном случае (и не преуспеет в выполнении). - person Jason Gross; 12.09.2017