ceps icon indicating copy to clipboard operation
ceps copied to clipboard

RFC: wishes for typeclasses

Open CohenCyril opened this issue 8 years ago • 19 comments

CohenCyril avatar Aug 31 '17 14:08 CohenCyril

Could we implement a cut on top of the engine actually, as a primitive ?

Isn't once the primitive ?

CohenCyril avatar Aug 31 '17 16:08 CohenCyril

In Prolog syntax, x :- a₀, ..., aₘ, !, b₀, ..., bₙ becomes Ltac once (a₀; ...; aₘ); b₀; ...; bₙ.

ppedrot avatar Aug 31 '17 16:08 ppedrot

In the end for a cut after the first argument it works with:

Class cut (class : Type) := Cut {cut_field : class}.
Notation "'cut c" := (@cut c) (at level 200).
Hint Extern 0 (@cut _) =>
  eapply @Cut; once typeclasses eauto : typeclass_instances.

But for a cut after n arguments, we need more encoding... (affaire à suivre)

CohenCyril avatar Aug 31 '17 16:08 CohenCyril

@CohenCyril No, that's just a generalization of the code I gave.

The code below encodes X :- A, B, !, C.

Class A := { a : nat }.
Class B := { b : nat }.
Class C := { c : nat }.

Arguments a _ : clear implicits.
Arguments b _ : clear implicits.
Arguments c _ : clear implicits.

Class X := { xa : nat; xb : nat; xc : nat }.

Instance a₀ : A := { a := 0 }.
Instance a₁ : A := { a := 1 }.

Instance b₀ : B := { b := 0 }.
Instance b₁ : B := { b := 1 }.

Instance c₀ : C := { c := 0 }.
Instance c₁ : C := { c := 1 }.

(** X :- A, B, !, C *)
Ltac instance :=
  (** Block instance resolution on C in the refine below *)
  change (id X);
  let x := fresh in
  let y := fresh in
  let z := fresh in
  once (
    unshelve refine (let x := _ in _); [exact A | | ];
    [typeclasses eauto|];
    unshelve refine (let y := _ in _); [exact B | | ];
    [typeclasses eauto|]
  );
  unshelve refine (let z := _ in _); [exact C | | ];
  [typeclasses eauto|];
  let av := eval unfold x in x in
  let bv := eval unfold y in y in
  let cv := eval unfold z in z in
  clear x y z;
  refine (Build_X av.(@a) bv.(@b) cv.(@c));
  change X
.

Ltac list_instance A :=
  try (
    let x := fresh in
    unshelve refine (let x := _ in _); [exact A | | ];
    [typeclasses eauto|];
    let ans := eval unfold x in x in
    clear x;
    idtac ans;
    fail
  ).

Hint Extern 2 X => instance : typeclass_instances.

Goal True.
list_instance A.
list_instance B.
list_instance C.
list_instance X.

ppedrot avatar Aug 31 '17 16:08 ppedrot

The code you are giving is a bit too ad-hoc for me, so I'm trying to build reusable components instead, like:

Class cut (class : Type) := Cut {cut_field : class}.
Notation "'cut c" := (@cut c) (at level 200).
Hint Extern 0 (@cut _) =>
  eapply @Cut; repeat (eapply @pair || eapply @existT);
  once typeclasses eauto : typeclass_instances.

CohenCyril avatar Aug 31 '17 16:08 CohenCyril

@CohenCyril Yes, but that's precisely why it'll fail. You're facing the expressivity wall of Ltac here. My encoding is pretty much systematic though. The instance tactic is easily accommodated to handle an arbitrary number of superclasses, cuts and an arbitrary body.

ppedrot avatar Aug 31 '17 16:08 ppedrot

@ppedrot Why do you say it will fail? It seems to work fine.

maximedenes avatar Aug 31 '17 16:08 maximedenes

I mean, managing to write a set of combinators to encode arbitrary cuts.

ppedrot avatar Aug 31 '17 16:08 ppedrot

@ppedrot Precisely. I wondered if with exceptions we could emulate this "long" once, but the hard part seems to be figuring out where the start of the cut is.

mattam82 avatar Aug 31 '17 20:08 mattam82

@mattam82 you are right, once does not implement cuts fully, because prolog cuts should also never consider then next rule/instance when the solution of the arguments before the cut are found...

CohenCyril avatar Sep 01 '17 08:09 CohenCyril

@CohenCyril I don't understand what you're saying. Do you have a Prolog example whose semantics is not the same as the Ltac one I gave above, and its intended semantics?

ppedrot avatar Sep 01 '17 08:09 ppedrot

@CohenCyril yep, that's another difference. It should be possible to implement globally in the search algorithm though. I wonder if you're adamant about having the "cut" in the type or if a separate [Hint Cut instance after 2 4 : typeclass_instances] would work as well?

mattam82 avatar Sep 01 '17 10:09 mattam82

@ppedrot here is the prolog example (from https://en.wikibooks.org/wiki/Prolog/Cuts_and_Negation slightly modified)

a(X) :- b(X), !, c(X).
a(X) :- d(X).

b(1).
b(3).

c(3).

d(4).

Which fails when asked ?- a(X).

But it succeeds when translated to Coq using my encoding:

Class cut (class : Type) := Cut {cut_field : class}.
Notation "'cut c" := (@cut c) (at level 200).
Hint Extern 0 (@cut _) =>
  eapply @Cut; repeat (eapply @pair || eapply @existT);
  once typeclasses eauto : typeclass_instances.

Class a (x : nat).
Class b (x : nat).
Class c (x : nat).
Class d (x : nat).

Instance a_bc x `{'cut b x} `{c x} : a x | 1.
Instance a_d x `{d x} : a x | 2.
Instance b1 : b 1 | 1.
Instance b3 : b 3 | 2.
Instance c3 : c 3.
Instance d4 : d 4.

Goal exists x, a x.
Proof.
eexists.
Set Typeclasses Debug.
typeclasses eauto.
Qed.

(Of course, removing the a(X) :- d(X). rule, the behavior is correct and the resolution fails in both prolog and Coq.)

@mattam82 I would be happy with a separate Hint Cut instruction, but maybe with named arguments instead of numbers, like Hint Cut instance x ! _ _ ! _ z. (a bit like the Arguments command to decide when to simplify).

CohenCyril avatar Sep 01 '17 11:09 CohenCyril

@CohenCyril that sounds right.

mattam82 avatar Sep 01 '17 11:09 mattam82

@mattam82 after discussing with @maximedenes and @gares, it would seem appropriate to go through another kind of Hint, Hint IfExtern taking two tactics cond and then as arguments that eauto would combine using IFCATCH cond then other_hints instead of ORELSE.

The point is to expose both the primitive ifte and once from the paper https://pdfs.semanticscholar.org/42eb/2d71af7e00356a41fae47c7752299dc6700d.pdf

CohenCyril avatar Sep 01 '17 12:09 CohenCyril

@CohenCyril Would that be enough to express cuts then?

mattam82 avatar Sep 01 '17 12:09 mattam82

@mattam82 cuts can be defined using IFCATCH (ONCE cond) th el, so at the Hint Extern level, it would indeed be enough. However, it would still be desirable to be able to annotate instances using cuts, which would translate to the appropriate combination of hints and once.

CohenCyril avatar Sep 01 '17 12:09 CohenCyril

@mattam82 And there still is the problem that relaunching typeclasses eauto starts a whole new search (losing potential options passed to the tactic, starting the trace from 1. again and interleaving the printing)...

CohenCyril avatar Sep 01 '17 12:09 CohenCyril

We implemented a first prototype here: https://github.com/mattam82/coq/tree/hint_extern_if

mattam82 avatar Oct 05 '17 11:10 mattam82