opaque

opaque は、定義に展開できない名前を宣言するコマンドです。具体的な実装を与えずに特定の型を持つ項が「とりあえず何かある」という表現ができます。

def との違い

def と異なり opaque コマンドを使用して宣言した名前は、#reduce コマンドで簡約できません。

-- def を使って定義
def greet : String := "hello world!"

-- reduce の結果と eval の結果が一致する

/-- info: "hello world!" -/
#guard_msgs in #eval greet

/-- info: "hello world!" -/
#guard_msgs in #reduce greet


-- opaque を使った定義
opaque opaque_greet : String := "hello world!"

-- 簡約できないので eval と一致しなくなる

/-- info: "hello world!" -/
#guard_msgs in #eval opaque_greet

/-- info: opaque_greet -/
#guard_msgs in #reduce opaque_greet

opaque で宣言された名前は partial で修飾された名前と同様に証明の中で簡約できなくなり、コンパイラを信頼しないとそれに関する証明ができなくなります。

-- 等しいものだという判定はできる
#eval opaque_greet == greet

-- 等しいことの証明が rfl ではできない
/--
error: tactic 'rfl' failed, the left-hand side
  opaque_greet
is not definitionally equal to the right-hand side
  greet
⊢ opaque_greet = greet
-/
#guard_msgs in example : opaque_greet = greet := by rfl

example : opaque_greet = greet := by
  -- greet は展開できるが
  dsimp [greet]

  -- opaque はできない。
  fail_if_success dsimp [opaque_greet]

  -- コンパイラを信頼することにすれば証明ができる
  native_decide

opaque で名前を宣言するとき、型だけを指定して値を指定しないということができます。このとき、その型が Inhabited 型クラスのインスタンスであることを暗黙のうちに使用します。

-- 値なしの宣言ができる
opaque some_string : String

-- 値は空文字列
/-- info: "" -/
#guard_msgs in #eval some_string

-- 適当な構造体を用意する
structure Something where
  val : String

-- Inhabited インスタンスがないのでエラーになる
/--
error: failed to synthesize
  Inhabited Something
Additional diagnostic information may be available using the `set_option diagnostics true` command.
-/
#guard_msgs in opaque something : Something

variable との違い

opaque と同じく variable コマンドも「特定の型を持つ項がとりあえず何かある」という表現をするために使用されることがありますが、両者には重要な違いがあります。

たとえば、Lean の型システムにおいて「自分自身に適用することができる関数 f : (A → B) → C は存在しない」ということをコードで確かめたかったとします。このとき「何でもいいので」型 A, B, C と関数 f が存在すると仮定を置きたくなります。

ここで型の宣言に variable を使用すると上手くいきません。

variable {A B C : Type} [Inhabited C]

opaque f : (A → B) → C

-- エラーにならない!
#check f f

これは、2つの f のそれぞれの引数としての A, B, C が異なる値を持ちうるためです。型を opaque で宣言すると固定されるので、想定通りエラーになります。

opaque A : Type
opaque B : Type
opaque C : Type

-- 実装は与えないが C は Inhabited のインスタンスだと仮定
instance : Inhabited C where
  default := sorry

opaque f : (A → B) → C

-- 想定通りエラーになる
/--
error: application type mismatch
  f f
argument
  f
has type
  (A → B) → C : Type
but is expected to have type
  A → B : Type
-/
#guard_msgs (error) in #check f f