DerivingHandler
Lean.Elab.DerivingHandler は、型クラスのインスタンスの自動生成を行うための関数の型で、Array Name → CommandElabM Bool という型と等しいものです。
import Lean
open Lean Elab Command
example : DerivingHandler = (Array Name → CommandElabM Bool) := by
rfl
deriving ハンドラの登録
deriving コマンドを使用したときに、対応する DerivingHandler 型の関数が呼び出されます。
簡単な例として、任意の型から Unit への自明な関数を提供する ToUnit という型クラスを考えてみましょう。この型クラスが与える変換は自明なので、どんな型に対しても実装方法は「わかりきって」おり自動生成できそうです。
/-- `Unit`への変換方法を与える自明な型クラス -/
class ToUnit (α : Type) where
toUnit : α → Unit
/-- `Nat`における `ToUnit` のインスタンス -/
instance : ToUnit Nat where
toUnit _ := ()
/-- `String`における `ToUnit` のインスタンス -/
instance : ToUnit String where
toUnit _ := ()
#guard ToUnit.toUnit 42 = ()
#guard ToUnit.toUnit "hello" = ()
この型クラスに対して deriving コマンドが使えるようにするには、まず以下のように記述したファイルを作成し、deriving ハンドラを登録します。
import Lean
import LeanByExample.Type.DerivingHandler.ToUnit
open Lean Elab Command
/-- `ToUnit` のためのインスタンス自動導出関数 -/
def deriveToUnitInstance (declNames : Array Name) : CommandElabM Bool := do
for declName in declNames do
let term := mkCIdent declName
let cmd ← `(command| instance : ToUnit $term := ⟨fun _ => ()⟩)
elabCommand cmd
return true
initialize
registerDerivingHandler ``ToUnit deriveToUnitInstance
そうすると、このファイルを import することで以下のように使用できるようになります。
import LeanByExample.Type.DerivingHandler.Lib
-- 最初はインスタンスがない
#check_failure ToUnit.toUnit true
-- `deriving` コマンドを使用
deriving instance ToUnit for Bool, Int, Char
-- インスタンスが生成された!
#guard ToUnit.toUnit true = ()
#guard ToUnit.toUnit (42 : Int) = ()
#guard ToUnit.toUnit 'a' = ()