新しい帰納型を作成するコマンドの一部としての Lean.Parser.Command.declaration : command
deriving
句には、その型からインスタンスが生成されるべきクラス名のカンマ区切りのリストを指定します:
optDeriving ::= (deriving ident,*)?
Lean は多くのクラスに対して自動的にインスタンスを生成することができます。これはインスタンスの 導出 (deriving)と呼ばれる処理です。インスタンスの導出は型を定義する時に呼び出すことも、独立したコマンドとして呼び出すこともできます。
新しい帰納型を作成するコマンドの一部としての Lean.Parser.Command.declaration : command
deriving
句には、その型からインスタンスが生成されるべきクラス名のカンマ区切りのリストを指定します:
optDeriving ::= (deriving ident,*)?
独立した Lean.Parser.Command.deriving : command
deriving
コマンドには複数のクラスと対象の名前を指定します。指定された各クラスは指定された各対象に対して導出されます。
command ::= ... | deriving instance ident,* for ident,*
このコードのように、複数の型に対して導出する複数のクラスを指定した場合:
structure A where
structure B where
deriving instance BEq, Repr for A, B
すべての型にすべてのインスタンスが存在するため、4つの Lean.Parser.Command.synth : command
#synth
コマンドはすべて成功します:
#synth BEq A
#synth BEq B
#synth Repr A
#synth Repr B
インスタンスの導出は 導出ハンドラ (deriving handler)のテーブルを使用し、型クラス名をインスタンスを導出するメタプログラムにマッピングします。導出ハンドラは registerDerivingHandler
を使用してテーブルに追加することができます。これは Lean.Parser.Command.initialize : command
initialize
ブロック内で呼び出す必要があります。それぞれの導出ハンドラは Array Name → CommandElabM Bool
型を持つ必要があります。ユーザがクラスのインスタンス導出を要求すると、登録されているハンドラが1つずつ呼び出されます。これらのハンドラにはインスタンスを導出させる相互ブロック内のすべての名前が提供され、正しくインスタンスを導出した場合は true
を、そうでない場合は何もせずに false
を返します。ハンドラが true
を返すと、それ以降のハンドラは呼び出されません。
Lean には以下のクラスの導出ハンドラがあります:
Lean.Elab.registerDerivingHandler (className : Name) (handler : DerivingHandler) : IO Unit
A DerivingHandler
is called on the fully qualified names of all types it is running for
as well as the syntax of a with
argument, if present.
For example, deriving instance Foo with fooArgs for Bar, Baz
invokes
fooHandler #[`Bar, `Baz] `(fooArgs)
.
IsEnum
クラスのインスタンスは型に対して適切なサイズの Fin
との間に全単射を提供することによって型が有限に列挙されることを示します:
class IsEnum (α : Type) where
size : Nat
toIdx : α → Fin size
fromIdx : Fin size → α
to_from_id : ∀ (i : Fin size), toIdx (fromIdx i) = i
from_to_id : ∀ (x : α), fromIdx (toIdx x) = x
帰納型は自明な列挙であり、コンストラクタがパラメータを期待しない帰納型ではこのクラスのインスタンスにおいては非常に繰り返しが多いコードになります。 Bool
のインスタンスが典型的です:
instance : IsEnum Bool where
size := 2
toIdx
| false => 0
| true => 1
fromIdx
| 0 => false
| 1 => true
to_from_id
| 0 => rfl
| 1 => rfl
from_to_id
| false => rfl
| true => rfl
導出ハンドラは IsEnum Bool
の実装をなぞるようにそれぞれのパターンのケースをプログラムで構築します:
open Lean Elab Parser Term Command
def deriveIsEnum (declNames : Array Name) : CommandElabM Bool := do
if h : declNames.size = 1 then
let env ← getEnv
if let some (.inductInfo ind) := env.find? declNames[0] then
let mut tos : Array (TSyntax ``matchAlt) := #[]
let mut froms := #[]
let mut to_froms := #[]
let mut from_tos := #[]
let mut i := 0
for ctorName in ind.ctors do
let c := mkIdent ctorName
let n := Syntax.mkNumLit (toString i)
tos := tos.push (← `(matchAltExpr| | $c => $n))
from_tos := from_tos.push (← `(matchAltExpr| | $c => rfl))
froms := froms.push (← `(matchAltExpr| | $n => $c))
to_froms := to_froms.push (← `(matchAltExpr| | $n => rfl))
i := i + 1
let cmd ← `(instance : IsEnum $(mkIdent declNames[0]) where
size := $(quote ind.ctors.length)
toIdx $tos:matchAlt*
fromIdx $froms:matchAlt*
to_from_id $to_froms:matchAlt*
from_to_id $from_tos:matchAlt*)
elabCommand cmd
return true
return false
initialize
registerDerivingHandler ``IsEnum deriveIsEnum