4

I'm writing a tactic that looks for the value associated to a key in a list of bindings. Something like:

Require Import String List Program.

Ltac assoc needle haystack :=
  match haystack with
  | @nil (_ * ?T) => constr:(@None T)
  | cons (?k, ?v) ?t => let pr := constr:(eq_refl k : k = needle) in constr:(Some v)
  | cons _        ?t => let res := assoc needle t in constr:res
  end.

Unfortunately, I do not know the exact form of the key; instead, I know a pattern that matches it. More precisely, the key that I'm looking for is the result of calling a type class method, but I don't know beforehand which instance will have been used. In the example below, I know that the key is a call to show "a", but I don't know with what instance:

Open Scope string_scope.
Open Scope list_scope.

Class Show A := { show: A -> string }.
Instance Show1 : Show string := {| show := fun x => x |}.
Instance Show2 : Show string := {| show := fun x => x ++ x |}.

Goal True.
  (* Works (poses Some 1) *)
  let v := assoc (show "a") [(show (Show := Show2) "a", 1); ("b", 2)] in pose v.
  (* Does not work (poses None) *)
  let v := assoc (show "a") [(show (Show := Show1) "a", 1); ("b", 2)] in pose v.

Is there a trick that I can use here, short of passing assoc an ltac that checks for a match? Ideally it would look something like (show (Show := _) "a"), or maybe (fun inst => show (Show := inst) "a").

Clément
  • 12,299
  • 15
  • 75
  • 115

1 Answers1

2

Looks like a passing a function works nicely, actually:

Ltac assoc needlef haystack :=
  match haystack with
  | @nil (_ * ?T) => constr:(@None T)
  | cons (?k, ?v) ?t => let pr := constr:(eq_refl k : k = needlef _) in constr:(Some v)
  | cons _        ?t => let res := assoc needlef t in constr:res
  end.

Goal False.
  let v := assoc (fun i => show (Show := i) "a") [(show (Show := Show2) "a", 1); ("b", 2)] in pose v.
  let v := assoc (fun i => show (Show := i) "a") [(show (Show := Show1) "a", 1); ("b", 2)] in pose v.
Clément
  • 12,299
  • 15
  • 75
  • 115