Zulip Chat Archive

Stream: lean4

Topic: Pretty Printing non-polymorphic instance


Leni Aniva (May 30 2023 at 09:57):

Is there a way to print polymorphic operators so they point to the exact instance being instantiated? e.g. In a context of n m : Nat, have n + m being prettyprinted as Nat.add n m?

I have this script for printing expressions: (It is a bit long but most of it deals with setting options)

import Lean

open Lean

def str_to_name (s: String): Name :=
  (s.splitOn ".").foldl Name.str Name.anonymous

def subroutine : Elab.TermElabM Unit := do
  let info? := ( MonadEnv.getEnv).find? (str_to_name "Nat.add_comm")
  match info? with
  | none => IO.println "Not found"
  | some info =>
    let format  Lean.Meta.ppExpr info.toConstantVal.type
    IO.println <| toString format

def setOptionFromString' (opts : Options) (entry : String) : IO Options := do
  let ps := (entry.splitOn "=").map String.trim
  let [key, val]  pure ps | throw $ IO.userError "invalid configuration option entry, it must be of the form '<key> = <value>'"
  let key := str_to_name key
  let defValue  getOptionDefaultValue key
  match defValue with
  | DataValue.ofString _ => pure $ opts.setString key val
  | DataValue.ofBool _   =>
    match val with
    | "true" => pure $ opts.setBool key true
    | "false" => pure $ opts.setBool key false
    | _ => throw $ IO.userError s!"invalid Bool option value '{val}'"
  | DataValue.ofName _   => pure $ opts.setName key val.toName
  | DataValue.ofNat _    =>
    match val.toNat? with
    | none   => throw (IO.userError s!"invalid Nat option value '{val}'")
    | some v => pure $ opts.setNat key v
  | DataValue.ofInt _    =>
    match val.toInt? with
    | none   => throw (IO.userError s!"invalid Int option value '{val}'")
    | some v => pure $ opts.setInt key v
  | DataValue.ofSyntax _ => throw (IO.userError s!"invalid Syntax option value")

def main (args: List String) : IO Unit := do
  let env: Environment  importModules
    (imports := ["Init"].map (λ str => { module := str_to_name str, runtimeOnly := false }))
    (opts := {})
    (trustLevel := 1)
  let coreM := Meta.MetaM.run' <| Elab.Term.TermElabM.run' subroutine
  let coreContext: Lean.Core.Context := {
    currNamespace := str_to_name "PrintExpr",
    openDecls := [],     -- No 'open' directives needed
    fileName := "<stdin>",
    fileMap := { source := "", positions := #[0], lines := #[1] }
    options :=  args.foldlM setOptionFromString' Lean.Options.empty
  }
  match  (coreM.run' coreContext { env := env }).toBaseIO with
  | .error exception =>
    IO.println s!"{← exception.toMessageData.toString}"
  | .ok a            =>
    IO.println "Finished"

When I run this with pp.explicit:

lean --run Examples/PrintExpr.lean pp.explicit=true

it gives

 (n m : Nat),
  @Eq Nat (@HAdd.hAdd Nat Nat Nat (@instHAdd Nat instAddNat) n m)
    (@HAdd.hAdd Nat Nat Nat (@instHAdd Nat instAddNat) m n)

How can I make this print Nat.add m n instead of HAdd?

Wojciech Nawrocki (May 30 2023 at 12:45):

The operation there is HAdd.hAdd so it wouldn't really make sense to pretty-print it as anything else. You could either normalize the term so that definitional unfolding gets you Nat.add (but it might normalize further without stopping at that), or write some custom code to see through type class instances and find out that in this case the instance points to Nat.add.

Max Nowak 🐉 (May 30 2023 at 12:48):

Maybe just beta-reducing @HAdd.hAdd Nat Nat Nat (instHAdd...) (without m and n) would be enough?

Max Nowak 🐉 (May 30 2023 at 12:50):

A general heuristic would be to unfold TC instance projections, but I'm not sure if that might be overzealous.

Wojciech Nawrocki (May 30 2023 at 13:05):

(Another way to put it is that pretty printing sometimes works modulo alpha-equivalence but does not do general definitional computation.)

Leni Aniva (May 30 2023 at 16:07):

Wojciech Nawrocki said:

The operation there is HAdd.hAdd so it wouldn't really make sense to pretty-print it as anything else. You could either normalize the term so that definitional unfolding gets you Nat.add (but it might normalize further without stopping at that), or write some custom code to see through type class instances and find out that in this case the instance points to Nat.add.

which normalisation function should I call?

Wojciech Nawrocki (May 31 2023 at 20:47):

@Leni V. Aniva what are you trying to do that needs this? It sounds like a bit of an #xy problem. There is a function here in LeanSMT that does a similar thing.

Leni Aniva (Jun 01 2023 at 02:52):

Wojciech Nawrocki said:

Leni V. Aniva what are you trying to do that needs this? It sounds like a bit of an #xy problem. There is a function here in LeanSMT that does a similar thing.

I promise this isn't an xy problem. I need the concrete instantiated functions to train ML algorithms

Mario Carneiro (Jun 01 2023 at 02:59):

printing the instance itself would be sufficient to disambiguate it if that's what you want

Leni Aniva (Jun 01 2023 at 03:04):

Mario Carneiro said:

printing the instance itself would be sufficient to disambiguate it if that's what you want

Do you mean with pp.instances? Even with these options set:

--pp.instances=true --pp.notation=false --pp.instanceTypes=true      20:03:03

I get this for Nat.add_comm, which does not show the instances

 (n m : Nat), Eq (HAdd.hAdd n m) (HAdd.hAdd m n)

Mario Carneiro (Jun 01 2023 at 03:04):

pp.explicit should work

Leni Aniva (Jun 01 2023 at 03:06):

Mario Carneiro said:

pp.explicit should work

if this is the only solution I'll just settle for

@HAdd.hAdd Nat Nat Nat (@instHAdd Nat instAddNat) n m

Mario Carneiro (Jun 01 2023 at 03:09):

I think that in general it's not really well defined what the answer is if you want something else

Leni Aniva (Jun 01 2023 at 03:09):

Mario Carneiro said:

I think that in general it's not really well defined what the answer is if you want something else

I see. Thanks! I'll just have to have the ML model work harder


Last updated: Dec 20 2023 at 11:08 UTC