native_decide
native_decide
は,式を評価したときに判ることを示すことができます.
たとえば,Lean では再帰関数 f
を定義したらそれが停止することの証明を求められますが,それを sorry
で回避したとしましょう.このとき f
の具体的な値を #eval
で評価することは依然として可能ですが,その値をとることを rfl
で証明することはできなくなります.
しかし,native_decide
を使うと証明が可能です.
/-- Euclide のアルゴリズム -/
def gcd (m n : Nat) : Nat :=
if m = 0 then
n
else
gcd (n % m) m
-- 停止性を証明しない
decreasing_by sorry
-- eval はできる
#eval gcd 42998431 120019
-- `rfl` では証明できない
-- これは停止性を証明していないため
#check_failure (by rfl : gcd 42998431 120019 = 1)
-- `native_decide` ならば証明できる
#check (by native_decide : gcd 42998431 120019 = 1)
補足すると,native_decide
を使用するときにはコンパイラを信頼することになります.具体的には Lean.ofReduceBool
という追加の公理が使用されます.
theorem native : Nat.gcd 42998431 120019 = 1 := by native_decide
/-- info: 'NativeDecide.native' depends on axioms: [propext, Lean.ofReduceBool] -/
#guard_msgs in #print axioms native
注意
native_decide
を使うことは安全ではなく,native_decide
を使うと簡単に False
を示すことができます.つまり,native_decide
を使った証明は正式な証明ではありません.
def one := 1
-- 間違った実装をわざと提供する
@[implemented_by one] def zero := 0
theorem zero_ne_eq_one : False := by
have : zero ≠ one := by decide
-- native_decide は implemented_by を真に受けるので,
-- 実際には間違いだが示せてしまう
have : zero = one := by native_decide
contradiction
/-- info: 'NativeDecide.zero_ne_eq_one' depends on axioms: [Lean.ofReduceBool] -/
#guard_msgs in #print axioms zero_ne_eq_one