Zulip Chat Archive

Stream: lean4

Topic: controlling elaboration order of tactics in terms


Rob Lewis (Nov 27 2023 at 02:54):

A student of mine has run into the following problem: using tactics to construct data, the order of elaboration can cause type checking to fail prematurely. His MWE is below. If you'll accept for the moment that using tactics to construct data is actually a reasonable thing to do in our application, is there anything we can do to tweak the elaboration order that might make this work?

inductive Dict : List (String × Type)  Type 1
  | nil : Dict []
  | cons {α hs nm} : α  Dict hs  Dict ((nm, α) :: hs)

-- This is Type-valued predicate
inductive List.MemT {α : Type _} : α  List α  Type _
  | hd {x xs} : List.MemT x (x :: xs)
  | tl {x xs y} : List.MemT x xs  List.MemT x (y :: xs)

def List.removeMem {α : Type _} {x} : (xs : List α)  List.MemT x xs  List α
  | _ :: xs, .hd => xs
  | x :: xs, .tl h => x :: removeMem xs h

def Dict.dropEntry {τ : Type} {hs : List (String × Type)} (nm : String) :
  Dict hs  (h : List.MemT (nm, τ) hs)  Dict (hs.removeMem h)
  | .cons x xs, .hd => xs
  | .cons x xs, .tl h' => .cons x (dropEntry nm xs h')

macro "mem" : tactic =>
  `(tactic| repeat (first | apply List.MemT.hd | apply List.MemT.tl))

def d1 : Dict [("a", Nat), ("b", Bool)] := .cons 4 (.cons true .nil)
def d2 : Dict [("a", Nat), ("b", Nat)] := .cons 3 (.cons 1 .nil)

-- These terms elaborate correctly...
#check d1.dropEntry "b" (by mem) -- Dict (List.removeMem [("a", Nat), ("b", Bool)] (List.MemT.tl List.MemT.hd))
#check d2.dropEntry "b" (by mem) -- Dict (List.removeMem [("a", Nat), ("b", Nat)] (List.MemT.tl List.MemT.hd))

-- And their types are definitionally equal
example : Dict (List.removeMem [("a", Nat), ("b", Bool)] (List.MemT.tl List.MemT.hd)) =
  Dict (List.removeMem [("a", Nat), ("b", Nat)] (List.MemT.tl List.MemT.hd)) :=
rfl

-- Type-correctness of this equality depends on the proof produced by `mem`
def p : Prop := d1.dropEntry "b" (by mem) = d2.dropEntry "b" (by mem) -- fails
def p' : Prop := d1.dropEntry "b" (.tl .hd) = d2.dropEntry "b" (.tl .hd) -- succeeds

Mario Carneiro (Nov 27 2023 at 03:06):

(d1.dropEntry "b" (by mem) :) = (d2.dropEntry "b" (by mem) :) works

Rob Lewis (Nov 27 2023 at 03:09):

My eye can only see a :smile: and mismatched parens -- I guess this is a type annotation without providing the type, that's different from : _ somehow?

Mario Carneiro (Nov 27 2023 at 03:09):

it's like : _ from lean 3

Mario Carneiro (Nov 27 2023 at 03:09):

in lean 4 : _ does nothing

Mario Carneiro (Nov 27 2023 at 03:09):

(e :) is basically the same as have := e; this where the have := is a separate tactic block

Mario Carneiro (Nov 27 2023 at 03:10):

that is, it elaborates e as much as possible, then asserts that the resulting type is equal to the expected type

Mario Carneiro (Nov 27 2023 at 03:10):

rather than using the expected type to influence elaboration

Rob Lewis (Nov 27 2023 at 03:11):

So, exactly what we want here. That's perfect, and I only thought to try : _. Thanks!


Last updated: Dec 20 2023 at 11:08 UTC