(* as it is dynamically inferred by simpl *)
Arguments minus !n / m.
simpl.
match goal with |- (match y with O => S x | S _ => _ end = 0) => idtac end.
Abort.
(* we avoid exposing a match *)
Arguments minus n m : simpl nomatch.
simpl.
match goal with |- (0 = 0) => idtac end.
Abort.
simpl.
match goal with |- (S x - y = 0) => idtac end.
Abort.
simpl.
match goal with |-(S x - (match y with O => _ | S _ => _ end) = 0) => idtac end.
Abort.
(* we unfold as soon as we have 1 args, but we avoid exposing a match *)
Arguments minus n / m : simpl nomatch.
simpl.
match goal with |- minus 0 = _ => idtac end.
Abort.
(* This does not work as one may expect. The point is that simpl is implemented as "strong (whd_simpl_state)" and after unfolding minus you have (fun m => match 0 => 0 | S n => ...) that is already in whd and exposes a match, that of course "strong" would reduce away but at that stage we don't know, and reducing by hand under the lambda is against whd *)
(* extra tuning for the usual heuristic *)
Arguments minus !n / m : simpl nomatch.
simpl.
match goal with |- (S x - y = 0) => idtac end.
Abort.
simpl.
match goal with |-(S x - (match y with O => _ | S _ => _ end) = 0) => idtac end.
Abort.
(* full control *)
Arguments minus !n !m /.
simpl.
match goal with |- (S x - y = 0) => idtac end.
Abort.
simpl.
match goal with |-(S x - (match y with O => _ | S _ => _ end) = 0) => idtac end.
Abort.
(* omitting /, that being immediately after the last ! is irrelevant *)
Arguments minus !n !m.
simpl.
match goal with |- (S x - y = 0) => idtac end.
Abort.
simpl.
match goal with |-(S x - (match y with O => _ | S _ => _ end) = 0) => idtac end.
Abort.
Definition pf (D1 C1 : Type) (f : D1 -> C1) (D2 C2 : Type) (g : D2 -> C2) :=
fun x => (f (fst x), g (snd x)).
Delimit Scope foo_scope with F.
Notation "@@" := nat (only parsing) : foo_scope.
Notation "@@" := (fun x => x) (only parsing).
Arguments pf {D1%F C1%type} f [D2 C2] g x : simpl never.

Lemma foo x : @pf @@ nat @@ nat nat @@ x = pf @@ @@ x.