Require Import Ltac2.Ltac2 Ltac2.Notations.

Ltac2 mutable foo () := constructor.

Goal True.
Proof.
foo ().
Qed.

Ltac2 Set foo := fun _ => fail.

Goal True.
Proof.
Fail foo ().
constructor.
Qed.


(** Bindings are dynamic *)

Ltac2 Type rec nat := [O | S (nat)].

Ltac2 rec nat_eq n m :=
  match n with
  | O => match m with | O => true | S _ => false end
  | S n => match m with | O => false | S m => nat_eq n m end
  end.

Ltac2 assert_eq n m :=
  match nat_eq n m with
  | true => ()
  | false => Control.throw Assertion_failure end.

Ltac2 mutable x := O.
Ltac2 y := x.
Ltac2 Eval (assert_eq y O).
Ltac2 Set x := (S O).
Ltac2 Eval (assert_eq y (S O)).

Ltac2 mutable quw := fun (n : nat) => O.
Ltac2 Set quw := fun n =>
  match n with
  | O => O
  | S n => S (S (quw n))
  end.

Ltac2 Eval (quw (S (S O))).

(** Not the right type *)
Fail Ltac2 Set foo := 0.

Ltac2 bar () := ().

(** Cannot redefine non-mutable tactics *)
Fail Ltac2 Set bar := fun _ => ().

(** Subtype check *)

Ltac2 rec h x := h x.

Ltac2 mutable f x := h x.
Fail Ltac2 Set f := fun x => x.

Ltac2 mutable g x := x.
Ltac2 Set g := h.

(** Rebinding with old values *)



Ltac2 mutable qux n := S n.

Ltac2 Set qux as self := fun n => self (self n).

Ltac2 Eval assert_eq (qux O) (S (S O)).

Ltac2 mutable quz := O.

Ltac2 Set quz as self := S self.

Ltac2 Eval (assert_eq quz (S O)).

Ltac2 rec addn n :=
  match n with
  | O => fun m => m
  | S n => fun m => S (addn n m)

  end.
Ltac2 mutable rec quy n :=
  match n with
  | O => S O
  | S n => S (quy n)
  end.

Ltac2 Set quy as self := fun n =>
                           match n with
                           | O => O
                           | S n => addn (self n) (quy n)
                           end.
Ltac2 Eval (assert_eq (quy (S (S O))) (S (S (S O)))).
Ltac2 Eval (assert_eq (quy (S (S (S O)))) (S (S (S (S (S (S O))))))).
