Zulip Chat Archive
Stream: new members
Topic: Failed to infer structural recursion: .brecOn mismatch
aron (Mar 15 2025 at 22:31):
I get this error on the below code:
failed to infer structural recursion:
Cannot use parameter uh:
application type mismatch
@IsValidUniVarKey.brecOn ↑uniVarsMap fun uv uh => (nextTypeVar : TypeVar) → funType uv nextTypeVar uh
argument
fun uv uh => (nextTypeVar : TypeVar) → funType uv nextTypeVar uh
has type
(uv : UniVar) → IsValidUniVarKey (↑uniVarsMap) uv → Type : Type 1
but is expected to have type
(a : UniVar) → IsValidUniVarKey (↑uniVarsMap) a → Prop : Type
I don't really understand this error. Is it saying that my inductive type is a Type
instead of a Prop
? Or vice versa?
The code:
import Std.Data.HashMap
import Mathlib
open Std
inductive Primitive where
| unit
| string
| int
| bool
| nat
deriving DecidableEq
structure TypeVar where
index : Nat
deriving DecidableEq
structure UnificationVar where
index : Nat
deriving Hashable, DecidableEq
instance : Min UnificationVar where
min a b := min a.index b.index |> UnificationVar.mk
instance : Max UnificationVar where
max a b := max a.index b.index |> UnificationVar.mk
def UnificationVar.toString (uv : UnificationVar) : String :=
"?" ++ uv.index.repr
abbrev UniVar := UnificationVar
/-- A type that does away with the frills, and is purpose built from the ground up for how we do inference: namely it only represents primitives, type constructors, and anything beyond that is a univar that references an `Option SimpleType`. in the UniVarsMap. -/
inductive SimpleType where
| primitive : Primitive → SimpleType
/-- A type name with the type variables -/
| typeCtor : String → List UniVar → SimpleType
instance instMembershipUniVarSimpleType : Membership UniVar SimpleType where
mem container item :=
match container with
| .primitive _ => False
| .typeCtor _ uniVars => uniVars.contains item
instance instMembershipUniVarOptionSimpleType : Membership UniVar (Option SimpleType) where
mem container item :=
match container with
| .some t => item ∈ t
| .none => False
instance instMembershipUniVarPrimitive : Membership UniVar Primitive where
mem _ _ := False
instance {a} : Membership UniVar (HashMap UniVar a) where
mem container item :=
container.contains item
@[simp] theorem SimpleType.mem_leaf_eq (x : Primitive) (k : UniVar) : (k ∈ SimpleType.primitive x) = False := rfl
@[simp] theorem SimpleType.mem_typeCtor_eq {ctor : String} (uniVars : List UniVar) (k : UniVar) : (k ∈ SimpleType.typeCtor ctor uniVars) = (k ∈ uniVars) := by
simp [instMembershipUniVarSimpleType]
abbrev SimpleTypeMap' :=
HashMap UniVar (Option SimpleType)
/-- The inductive predicate that a uniVar is a valid key in a `SimpleTypeMap'`, that every univar at a value in the map is also a valid key in the map, and that there are no cycles of univars looping round to point to each other! -/
inductive IsValidUniVarKey (m : SimpleTypeMap') : UniVar → Prop where
| mk (uniVar : UniVar)
(hmem : uniVar ∈ m)
(h : ∀ uv ∈ m[uniVar]'hmem, IsValidUniVarKey m uv) : IsValidUniVarKey m uniVar
/-- The predicate that a type is in the map and that all univars in that type are also in the map -/
inductive ContainsValidUniVars (m : SimpleTypeMap') : SimpleType → Prop where
| mk (t : SimpleType)
(h : ∀ uv ∈ t, IsValidUniVarKey m uv) : ContainsValidUniVars m t
def ContainsValidUniVars.mkFromPrimitive (m : SimpleTypeMap') (p : Primitive) : ContainsValidUniVars m (SimpleType.primitive p) := by
refine .mk (SimpleType.primitive p) ?_
intro uv huv
simp only [instMembershipUniVarSimpleType] at huv
def ContainsValidUniVars.mkFromKnownPrimitive (m : SimpleTypeMap') {t : SimpleType} (p : Primitive) (h : t = SimpleType.primitive p) : ContainsValidUniVars m t := by
rw [h]
exact .mkFromPrimitive m p
def ContainsValidUniVars.mkFromEmptyCtor (ctor : String) {t : SimpleType} (m : SimpleTypeMap') (h : t = SimpleType.typeCtor ctor []) : ContainsValidUniVars m t := by
rw [h]
refine .mk (SimpleType.typeCtor ctor []) ?_
intro uv huv
simp [instMembershipUniVarSimpleType] at huv
@[simp] theorem SimpleType.uniVar_nin_primitive {uv : UniVar} {p : Primitive} : (uv ∈ SimpleType.primitive p) = False := by
simp [instMembershipUniVarSimpleType]
@[simp] theorem SimpleType.uniVar_in_typeCtor {uv : UniVar} {ctor : String} {uniVars : List UniVar} : (uv ∈ SimpleType.typeCtor ctor uniVars) = (uv ∈ uniVars) := by
simp [instMembershipUniVarSimpleType]
def makePrfUniVarInPrimitive {p : Primitive} (uv : UniVar) (t : SimpleType) (h : t = SimpleType.primitive p) (hh : uv ∈ t) : False := by
simp [instMembershipUniVarSimpleType] at hh
rw [h] at hh
simp at hh
def SimpleTypeMap := { map : SimpleTypeMap' // ∀ uv ∈ map, IsValidUniVarKey map uv }
inductive SimpleZonkedType where
| primitive : Primitive → SimpleZonkedType
/-- A type variable. Might be slightly tricky to make sure we keep typevars that should be the same skolem the same, whilst ensuring that typevars that can be different are different.
But what I'm thinking rn is that at every zonking step we return the next available typevar index, and when we learn that we can generalise a univar, we replace that with this next available typevar index. At which point we create the next typevar by incrementing the index of the last one, and bubble that one up the callers.
-/
| typeVar : TypeVar → SimpleZonkedType
| typeCtor : String → List SimpleZonkedType → SimpleZonkedType
abbrev SimpleZonkedTypeMap := HashMap UniVar SimpleZonkedType
def HashMap.attach {k v : Type u} [DecidableEq k] [Hashable k] (map : HashMap k v) : List {key : k // key ∈ map} :=
map.keys.attach.map fun ⟨key, hkey⟩ => ⟨key, HashMap.mem_keys.mp hkey⟩
/-- We go through the map one univar key at a time, and zonk it until it can no longer be zonked. At which point by the end we should have a map of fully zonked types, which should be trivially convertible to a `SimpleZonkedTypeMap` I believe. -/
def zonkSimpleTypeMap (uniVarsMap : SimpleTypeMap) : SimpleZonkedTypeMap :=
let firstTypeVar : TypeVar := .mk 0
let allUniVars := uniVarsMap.1 |> HashMap.attach
let map :=
allUniVars.foldl (init := (HashMap.empty, firstTypeVar)) (fun (acc, accTypeVar) ⟨uv, uvInMap⟩ =>
let isValidUniVarKey := uniVarsMap.2 uv uvInMap
let zonked := zonkSingleUv uv accTypeVar isValidUniVarKey
(acc.insert uv zonked.1, zonked.2)
)
map.1
where
-- ok so i don't think this will really work because just focusing on one univar at a time will probably not let us generalise the univars to typevars consistently across the thing, but I think let's try this just for one univar at a time and see how we go from there.
-- @TODO: need to prove termination on `zonkSimpleType`, and need to do so by feeding in an inductive as an argument, somehow. Should probably be able to derive this from an `IsValidUniVarKey` somehow
zonkSingleUv (uv : UniVar) (nextTypeVar : TypeVar) (uh : IsValidUniVarKey uniVarsMap.1 uv) : SimpleZonkedType × TypeVar :=
let hmem :=
match uh with
| IsValidUniVarKey.mk _ hmem _ => hmem
match hh : uniVarsMap.1[uv]'hmem with
| none =>
(SimpleZonkedType.typeVar nextTypeVar, { nextTypeVar with index := nextTypeVar.index + 1 })
| some someVal =>
let h : ContainsValidUniVars uniVarsMap.1 someVal :=
by
cases hhh : someVal with
| primitive p =>
refine .mkFromKnownPrimitive uniVarsMap.1 p ?_
rfl
| typeCtor tag uniVars =>
cases univarsPrf : uniVars with
| nil =>
refine .mkFromEmptyCtor tag uniVarsMap.1 ?_
rewrite [univarsPrf] at hhh
rfl
| cons hd tl =>
have tt : ∀ uv_1 ∈ uniVarsMap.1[uv], IsValidUniVarKey uniVarsMap.1 uv_1 :=
match uh with
| IsValidUniVarKey.mk _ _ tt => tt
rw [← univarsPrf, ← hhh]
constructor
intro thisUv huv
simp [instMembershipUniVarOptionSimpleType] at huv
rw [hh] at tt
have := tt thisUv huv
exact this
match hh : someVal with
| .primitive p => (SimpleZonkedType.primitive p, nextTypeVar)
| .typeCtor ctor uniVars =>
let zonkedArgs :=
uniVars.attach.foldl (init := ([], nextTypeVar))
fun (acc, accTypeVar) ⟨thisUv, thisUvInUniVars⟩ =>
by
let zonked := zonkSingleUv thisUv accTypeVar ?_
refine (zonked.1 :: acc, zonked.2)
have huv :=
match h with
| .mk _ huv => huv
rw [hh] at huv
have : thisUv ∈ SimpleType.typeCtor ctor uniVars → IsValidUniVarKey uniVarsMap.1 thisUv := huv thisUv
simp [SimpleType.uniVar_in_typeCtor] at this
exact this thisUvInUniVars
(SimpleZonkedType.typeCtor ctor zonkedArgs.1, zonkedArgs.2)
termination_by structural uh
Aaron Liu (Mar 15 2025 at 23:00):
It says that when doing structural recursion on IsValidUniVarKey
, you can only eliminate into Prop
.
aron (Mar 15 2025 at 23:01):
what does that mean? that I can't recurse on IsValidUniVarKey
inside a function unless that function returns a Prop
? why?
Aaron Liu (Mar 15 2025 at 23:02):
It means you can't recurse on IsValidUniVarKey
unless you're proving a theorem. This is because IsValidUniVarKey
is a Prop
, and so is subject to proof irrelevance.
Aaron Liu (Mar 15 2025 at 23:03):
Now that I'm looking at IsValidUniVarKey
, you may be able to modify its definition a little to make this work.
aron (Mar 15 2025 at 23:07):
when I change the two proofs to return Type
s instead, Lean seems to go into an infinite loop and doesn't show me any errors even when I write some nonsense code :thinking:
Screenshot 2025-03-15 at 23.06.19.png
Last updated: May 02 2025 at 03:31 UTC