Zulip Chat Archive

Stream: lean4

Topic: Type Inference Problem


Marcus Rossel (Oct 21 2022 at 14:25):

I have a scenario where I feel like it should be possible for Lean to infer a type, but it fails.
The setup is as follows:

structure Scheme where
  labels : Type
  type : labels  Type

structure Schemes where
  a : Scheme
  b : Scheme

structure Values (schemes : Schemes) where
  a : (l : schemes.a.labels)  schemes.a.type l
  b : (l : schemes.b.labels)  schemes.b.type l

def SchemeM (schemes : Schemes) (α : Type) :=
  Values schemes  (Values schemes) × α

def getA {schemes : Schemes} (l : schemes.a.labels) : SchemeM schemes (schemes.a.type l) :=
  fun values => (values, values.a l)

That is, getA is an operation in the SchemeM monad which takes a given label l and returns the value for that label.
The following demonstrates using this operation:

inductive A | a₁ | a₂

abbrev A.scheme : Scheme where
  labels := A
  type
    | a₁ => Nat
    | a₂ => String

def Empty.scheme : Scheme where
  labels := Empty
  type := (nomatch ·)

def exampleSchemes : Schemes where
  a := A.scheme
  b := Empty.scheme

example : SchemeM exampleSchemes Nat :=
  getA A.a₁ -- error

The problem is that this fails on the last line with:

application type mismatch
  getA A.a₁
argument
  A.a₁
has type
  A : Type
but is expected to have type
  ?m.29155.a.labels : Type

I can fix the issue by explicitly providing the implicit schemes parameter:

example : SchemeM exampleSchemes Nat :=
  getA (schemes := exampleSchemes) A.a₁

But don't understand why type inference is failing, as the return type of the example already explicitly states this (at least according to my vague understanding of how Lean might infer such a parameter).

Also, if I split up the Schemes and instead write all of the schemes individually, it works:

structure Scheme where
  labels : Type
  type : labels  Type

structure Values (schemeA schemeB : Scheme) where
  a : (l : schemeA.labels)  schemeA.type l
  b : (l : schemeB.labels)  schemeB.type l

def SchemeM (schemeA schemeB : Scheme) (α : Type) :=
  (Values schemeA schemeB)  (Values schemeA schemeB) × α

def getA {schemeA schemeB : Scheme} (l : schemeA.labels) : SchemeM schemeA schemeB (schemeA.type l) :=
  fun values => (values, values.a l)

inductive A | a₁ | a₂

abbrev A.scheme : Scheme where
  labels := A
  type
    | a₁ => Nat
    | a₂ => String

abbrev Empty.scheme : Scheme where
  labels := Empty
  type := (nomatch ·)

example : SchemeM A.scheme Empty.scheme Nat :=
  getA A.a₁

Is there any way to make the first version work, though?

Sebastian Ullrich (Oct 21 2022 at 14:59):

Right, this is a restriction in how Lean uses the expected type to infer implicit parameters: it only does that when the return type does not mention any explicit parameters. So the presence of l in the return type of getA prevents schemes to be inferred as well. There are a lot of such restrictions documented at https://github.com/leanprover/lean4/blob/9fd433785b35a4a309022db05ec6074da64da209/src/Lean/Elab/App.lean#L273-L310.

Marcus Rossel (Oct 21 2022 at 15:19):

@Sebastian Ullrich How does the inference of schemeA in the second version of getA work then? The return type also references schemeA.type l.


Last updated: Dec 20 2023 at 11:08 UTC