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! 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