variable

variable は、定理や関数の引数を宣言するためのコマンドです。

たとえば以下の関数と命題に対して、引数の α : Typel : List α は共通です。

/-- リストが空であるか判定する -/
def isNil {α : Type} (l : List α) : Bool :=
  match l with
  | [] => true
  | _ => false

theorem nng_list_length {α : Type} (l : List α) : l.length ≥ 0 := by simp

variable コマンドを利用すると、共通の引数を宣言してまとめておくことができます。

variable {α : Type} (l : List α)

def isNil : Bool :=
  match l with
  | [] => true
  | _ => false

theorem nng_list_length : l.length ≥ 0 := by simp

theorem と def での取り込み基準の違い

variable コマンドの挙動は theoremdef で異なります。

theorem コマンドに対しては、定理の型(つまり主張)に現れる変数だけを自動で引数にします。 一方で、def コマンドに対しては、本体に現れる変数も自動で引数にします。

variable (n : Nat)

-- 定理の主張には `m` しか現れていないので、`n` は引数として取り込まれない
/-
error: Unknown identifier `n`
-/
theorem foo (m : Nat) : m = m :=
  have : n = n := by rfl
  rfl

-- 引数は1つだけ
#check (foo : ∀ m : Nat, m = m)

-- `n` は証明の中にしか現れないが、引数として取り込まれる
def foo' (m : Nat) : m = m :=
  have : n = n := by rfl
  rfl

-- その証拠に `foo'` には引数が2つある
#check (foo' : ∀ (_n m : Nat), m = m)

明示的に引数に含めるには、include コマンドを使用します。

variable (n : Nat)

include n

theorem foo_included (m : Nat) : m = m :=
  have : n = n := by rfl
  rfl

-- `n` が引数として含まれた結果
-- 引数が2つになっている
#check (foo_included : ∀ (_n m : Nat), m = m)

再帰と variable

variable コマンドで宣言された引数は、再帰呼び出しの中でも同じ値が使用されます。 したがって、再帰関数の再帰呼び出しで変化する引数を variable で共通化することはできません。

variable {α : Type} (l : List α)

def List.myLength : Nat :=
  match l with
  | [] => 0
  | _ :: xs => by
    -- `List.myLength` の型が `List α → Nat` ではなくて、
    -- `Nat` になってしまっている。
    -- 引数の `List α` の部分には固定された値が入ってしまっているため
    guard_hyp List.myLength : Nat

    sorry