Zulip Chat Archive
Stream: new members
Topic: Syntactically separate goals from code
MrQubo (Feb 27 2025 at 18:45):
Is it possible in lean to syntactically separate proof obligations from the code?
As an example, with the proof obligation here I know of two ways I could write that proof:
inductive T
| t1 : T
| t2 : Array Nat → T
def foo : T → Nat
| T.t1 => 0
| T.t2 arr => arr[0]'(by admit)
or
inductive T
| t1 : T
| t2 : Array Nat → T
def foo : T → Nat
| T.t1 => 0
| T.t2 arr =>
have h : 0 < arr.size := by admit
arr[0]'h
But in both cases the proof is written inline together with the code. Is it possible to somehow separate it? E.g. something like that?
inductive T
| t1 : T
| t2 : Array Nat → T
def foo : T → Nat
| T.t1 => 0
| T.t2 arr => arr[0]'proof1
where
proof1 := by admit
Or even better, without having to give a name to the proof.
Also, the question is not tied to array access only, I would want something like that for any Prop autoParam.
Mauricio Collares (Feb 27 2025 at 19:30):
It's a bit subjective. Do you think the following is a solution?
inductive T
| t1 : T
| t2 : Array Nat → T
def foo : T → Nat
| T.t1 => 0
| T.t2 arr => by
refine arr[0]'?_
· sorry
Bjørn Kjos-Hanssen (Feb 27 2025 at 19:32):
How about this...
import Mathlib
lemma prove {arr : Array Nat} : 0 < arr.size := by admit
inductive T
| t1 : T
| t2 : Array Nat → T
def foo : T → Nat
| T.t1 => 0
| T.t2 arr => arr[0]'prove
MrQubo (Feb 27 2025 at 19:35):
@Mauricio Collares Not really. You could imagine there's more code after arr[0]
, or one more constructor and case T.t3
.
Aaron Liu (Feb 27 2025 at 19:36):
inductive T
| t1 : T
| t2 : Array Nat → T
def foo : T → Nat
| T.t1 => 0
| T.t2 arr => arr[0]'prove
where
prove {arr : Array Nat} : 0 < arr.size := by admit
Mauricio Collares (Feb 27 2025 at 19:36):
MrQubo said:
Mauricio Collares Not really. You could imagine there's more code after
arr[0]
, or one more constructor and caseT.t3
.
Can you give an example? I'm not sure I'll be able to help, but I think it will help others understand the constraints. With one more constructor, you could do something like
inductive T
| t1 : T
| t2 : Array Nat → T
| t3 : Array Nat → T
def foo : T → Nat := by
refine fun x =>
match x with
| T.t1 => 0
| T.t2 arr => arr[0]'?_
| T.t3 arr => arr[1]'?_
· sorry
· sorry
MrQubo (Feb 27 2025 at 19:43):
Mauricio Collares said:
MrQubo said:
Mauricio Collares Not really. You could imagine there's more code after
arr[0]
, or one more constructor and caseT.t3
.Can you give an example? I'm not sure I'll be able to help, but I think it will help others understand the constraints
Yes, sorry, here's an example:
inductive T
| t1 : T
| t2 : Array Nat → T
| t3 : T
def foo : T → Nat
| T.t1 => 0
| T.t2 arr => arr[0]'(by admit) + arr[1]'(by admit)
| T.t3 => 0
I would want something like:
def foo : T → Nat
| T.t1 => 0
| T.t2 arr => arr[0]'?_ + arr[1]'?_
| T.t3 => 0
. by admit
. by admit
As to why I want it. My goal is to make the functions easier to read. Usually I wouldn't care about proofs if I want to read the function to understand what it does.
MrQubo (Feb 27 2025 at 19:44):
Ah, yes, I can refine the whole body of a function, that looks promising!
Kyle Miller (Feb 27 2025 at 21:19):
I imagine one day there will be some real syntax for this. I understand Coq has such a feature.
Imagine a proofs
clause, something like
def foo : T → Nat
| T.t1 => 0
| T.t2 arr => arr[0]'?pf1 + arr[1]'?pf2
| T.t3 => 0
proofs
case pf1 => sorry
case pf2 => sorry
MrQubo (Feb 27 2025 at 23:02):
Kyle Miller said:
I imagine one day there will be some real syntax for this. I understand Coq has such a feature.
Imagine a
proofs
clause, something likedef foo : T → Nat | T.t1 => 0 | T.t2 arr => arr[0]'?pf1 + arr[1]'?pf2 | T.t3 => 0 proofs case pf1 => sorry case pf2 => sorry
I implemented this with a macro:
import Lean
section
open Lean
macro d:Parser.Command.declaration "\nproofs\n" proofs:Parser.Tactic.tacticSeqIndentGt : command => do
let proofs : TSyntax ``Parser.Tactic.tacticSeq := ⟨proofs⟩
let d := d.raw
let decl := d[1]
-- TODO: abbrev, opaque, example
if decl.isOfKind ``Parser.Command.definition then
-- "def " >> declId >> optDeclSig >> declVal >> optDefDeriving
let mut declVal : Syntax := decl[3]
if declVal.isOfKind ``Parser.Command.declValSimple then
-- leading_parser " :=\n" >> termParser >> Termination.suffix >> optional Term.whereDecls
let term ← Elab.Term.expandWhereDeclsOpt declVal[3] declVal[1]
let term : TSyntax `term := ⟨term⟩
let term ← `(by refine $term; ($proofs))
declVal := declVal.setArg 1 term
declVal := declVal.setArg 3 mkNullNode
else if declVal.isOfKind ``Parser.Command.declValEqns then
-- leading_parser Term.matchAltsWhereDecls
let declValEqns := declVal[0]
-- leading_parser matchAlts >> Termination.suffix >> optional whereDecls
let term ← Elab.Term.expandMatchAltsWhereDecls declValEqns
let term : TSyntax `term := ⟨term⟩
let term ← `(by refine $term; ($proofs))
let terminationSuffix := declValEqns[1]
declVal := mkNode ``Parser.Command.declValSimple #[mkAtom ":=", term, terminationSuffix, mkNullNode]
else
Macro.throwUnsupported
let decl := decl.setArg 3 declVal
let d := d.setArg 1 decl
return ⟨d⟩
else
Macro.throwUnsupported
end
inductive T
| t1 : T
| t2 : (arr : Array Nat) → arr.size > 1 → T
| t3 : T
def foo : T → Nat
| T.t1 => 0
| T.t2 arr _ => arr[0]'?pf1 + arr[1]'?pf2
| T.t3 => 0
proofs
case pf1 => omega
case pf2 => omega
Tomaz Mascarenhas (Mar 03 2025 at 16:27):
Kyle Miller said:
I understand Coq has such a feature.
Indeed, in Coq there is a Program Definition
constructor which defers all proof obligations to after you've defined your function, which I think is exactly what is being asked here: https://coq.inria.fr/doc/v8.9/refman/addendum/program.html#program-definition
MrQubo (Mar 03 2025 at 17:52):
Tomaz Gomes said:
Indeed, in Coq there is a
Program Definition
constructor which defers all proof obligations to after you've defined your function, which I think is exactly what is being asked here: https://coq.inria.fr/doc/v8.9/refman/addendum/program.html#program-definition
I don't know Coq too much, but indeed, I think that's what I'm asking about.
To apply it to Lean specifically, I think that every unsolved autoParam should generate a proof obligation automatically. And maybe even every implicit param.
Last updated: May 02 2025 at 03:31 UTC