Open5
Lean4のマクロで、文字列リテラルを、Lean4のコードとしてパースして展開する
文字列リテラル "∀a b, a → b → a ∧ b"
に対して、
theorem propA : myterm["∀a b, a → b → a ∧ b"] :=
fun {a b : Prop} (ha : a) (hb : b) => ⟨ha,hb⟩
と定義することで、propA
の型が∀a b, a → b → a ∧ b
となるようなマクロ(すなわち、文字列リテラルをLeanのコードとしてパースして展開するマクロmyterm["ここにLeanのコードを書く"]
)をつくった。
実装したコードは
elab "myterm[" s:str "]" : term => do
let env ← getEnv
let parsedSyntax ← match Lean.Parser.runParserCategory env `term s.getString with
| Except.ok stx => pure stx
| Except.error errmsg => throwError errmsg
let prop ← elabTerm parsedSyntax none
pure prop
背景
大目標は、文字列リテラルの中の日本語をLeanで証明すること。
日本語を自然言語処理して論理式に変換するソフトウェアはすでに存在するので、
残す作業は
- 外部のプロセスとの通信を行う方法
- Leanのコードが書いてある文字列リテラルをパースして、その場に展開する方法
- 上記2つをビルド前に行い、定理証明のときには出力された論理式を扱えるようにする方法
だった。いろいろ調べた結果、1. はIOモナド、2. はコンパイラの実装に使われている内部の関数たち、3. はElaboration(いわゆるマクロのうちの一つ)によって実現できることがわかった。
Elaboration(今回使用したのはTermElabM
というモナド)はマクロのようなもので、IOモナドを含む様々な関数の実行をコンパイル前に行うため、定理証明のときには実行結果が利用できる。
2. を切り出すと表題のような処理になる。
このコードの大半は2. の部分の処理で、Lean.Parser.runParserCategory
によって文字列リテラルをLean.Syntax
(原始的な構文木)にパースしたあと、Lean.Elab.elabTerm
によってLean.Expr
(マクロの展開などのメタプログラミングに関する処理が終了した、Leanのコアが処理可能な構文木)に変換する。
参考
-
A Lean 4 Metaprogramming BookのTerm Elaborationの部分
-
elabTerm
を使ってLean.Expr
を作っている部分
-
-
Add your own Custom MonadLift
- あるモナドの中で、複数の異なるモナドが実行できるような仕組みとしてMonadLiftというのものが存在する
- 上のコードで言うと、elabの中でIOモナドが実行できるのはこれのおかげ