Zulip Chat Archive
Stream: lean4
Topic: Obtaining types of structure fields fields
Henrik Böving (Sep 25 2021 at 22:32):
I've been trying to get some very basic things to work for my doc gen tool based on the thing @Mac provided me with and am currently attempting to get information about a structure, especially its fields and their types. My current code is basically this:
def envOfFile (path : FilePath) : IO Environment := do
let opts := Options.empty
let mainModuleName := `main
let input ← IO.FS.readFile path
let (env, ok) ← Elab.runFrontend input opts path.toString mainModuleName
unless ok do throw <| IO.userError "could not elaborate file"
return env
def prettyPrintStructures (path : FilePath) : IO PUnit := do
let env ← envOfFile path
for struct in structureExt.getEntries env do
IO.println <| "structure " ++ struct.structName.toString
for info in struct.fieldInfo do
match env.find? info.projFn with
| some constInfo => IO.println <| " " ++ info.fieldName.toString ++ " : " ++ constInfo.type.dbgToString
| none => IO.println "Should not happen"
and while it does correctly find the structures and their fields the type of the projection functions are of course not the actual types of the fields. Now I'm wondering whether I've just overlooked some function in the Lean package that lets me get these types or whether the best way to obtain this would basially be to walk the Expr
I get from constInfo.type
and remove the outer most foralls (since these will be used for generic structures like for example with: forall {α : Type} {β : Type}, (Point α β) -> β
+ the left part of the arrow I find after getting rid of the foralls and use that as the type of the field....or whether there is a third and better way I'm not aware of to do this.
Wojciech Nawrocki (Apr 28 2022 at 02:10):
Seconded, I wasn't able to find functionality for this. Here is an attempt that works for direct fields (i.e. no subobject fields). Getting it to work for parent fields similarly to getStructureFieldsFlattened
would be more work since Lean does not generate projections for those, so I think we would have to recursively process parent structures.
def getProjFnInfoForField? (env : Environment) (structName : Name) (fieldName : Name) : Option (Name × ProjectionFunctionInfo) :=
if let some projFn := getProjFnForField? env structName fieldName then
(projFn, ·) <$> env.getProjectionFnInfo? projFn
else
none
/-- Like `getStructureFields`, but include also the types of fields. Note that when the structure type
has generic parameters, the field types will contain unassigned mvars. These are returned in the second
tuple component. -/
def getTypedStructureFields (structName : Name) : MetaM (Array (Name × Array Expr × Expr)) := do
let env ← getEnv
let fields := getStructureFields env structName
let mut ret := #[]
for field in fields do
let some (projFn, projInfo) := getProjFnInfoForField? env structName field
| throwError "'{structName}.{field}' has no associated projection function"
let tp ← ConstantInfo.type <$> getConstInfo projFn
-- make mvars for parameters and the structure argument
let (mvars, _, cod) ← forallMetaBoundedTelescope tp (projInfo.numParams + 1)
-- forget the structure argument mvar
ret := ret.push (field, mvars.pop, cod)
return ret
Wojciech Nawrocki (Apr 28 2022 at 02:12):
structure Foo (β : Type) where
a : Nat
b : β → β
structure Foo₂ (β : Type) extends Foo β where
c : String
d : β → ∀ α, Foo α
elab "TEST" n:ident : command => do IO.println (← liftTermElabM none do getTypedStructureFields n.getId)
/- #[(toFoo, (#[?_uniq.11475], Foo ?_uniq.11475)), (c, (#[?_uniq.11477], String)), (d, (#[?_uniq.11479], ?_uniq.11479 -> (forall (α : Type), Foo α)))] -/
TEST Foo₂
Henrik Böving (Apr 28 2022 at 08:47):
The way this is done right now in doc-gen4 (this is the way I think @Gabriel Ebner recommended to me) is by parsing the type of the actual constructor like this: https://github.com/leanprover/doc-gen4/blob/main/DocGen4/Process.lean#L257-L264
Wojciech Nawrocki (Apr 28 2022 at 19:00):
I see, thanks! Looking at the constructor is a good solution to share the index mvars between field types.
Leonardo de Moura (Apr 29 2022 at 12:14):
Here is a suggestion.
import Lean
open Lean
open Lean.Meta
/--
Execute `k` with an array containing pairs `(fieldName, fieldType)`.
`k` is executed in an updated local context which contains local declarations for the `structName` parameters.
-/
def withFields (structName : Name) (k : Array (Name × Expr) → MetaM α) (includeSubobjectFields := true) : MetaM α := do
let .inductInfo info ← getConstInfo structName | throwError "'{structName}' is not a structure"
let us := info.levelParams.map mkLevelParam
forallTelescopeReducing info.type fun params _ =>
withLocalDeclD `s (mkAppN (mkConst structName us) params) fun s => do
let mut info := #[]
for fieldName in getStructureFieldsFlattened (← getEnv) structName includeSubobjectFields do
let proj ← mkProjection s fieldName
info := info.push (fieldName, (← inferType proj))
k info
def printFieldNames (structName : Name) (includeSubobjectFields := true) : MetaM Unit :=
withFields (includeSubobjectFields := includeSubobjectFields) structName fun fieldTypes =>
for (fieldName, fieldType) in fieldTypes do
IO.println s!"{fieldName} : {← ppExpr fieldType}"
structure Foo (β : Type) where
a : Nat
b : β → β
structure Foo₂ (β : Type) extends Foo (β × β) where
c : String
d : β → ∀ α, Foo α
#eval printFieldNames ``Foo₂
/-
toFoo : Foo (β × β)
a : Nat
b : β × β → β × β
c : String
d : β → (α : Type) → Foo α
-/
#eval printFieldNames (includeSubobjectFields := false) ``Foo₂
/-
a : Nat
b : β × β → β × β
c : String
d : β → (α : Type) → Foo α
-/
Last updated: Dec 20 2023 at 11:08 UTC