一键导入
isabelle-scala-plugin
How to write Isabelle/Scala plugins (Scala.Fun) that ML can call via PIDE protocol
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
How to write Isabelle/Scala plugins (Scala.Fun) that ML can call via PIDE protocol
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Operational pipeline to start, monitor, STOP, and recover the MathBench missing-lemma loop running in multi-node fleet mode on the MBZUAI cluster (watcher + slurmx fleet REPLs + login-node RPC host + serial adjudication + phase-2 import reconciliation). Use when launching/resuming the fleet run, stopping it cleanly, recovering after a crash, or applying the hard-won operational rules (correct kill sequence, git safety on the shared checkout, shell/conda/timeout fixes, re-collection prerequisite). For the single-machine AoA×PutnamBench eval use `aoa-putnam-eval` instead; for the design rationale of the loop see `MISSING_LEMMA_LOOP.md`.
How to refresh the local semantic embedding database (~1.5 GB of LMDB at ~/.cache/Isabelle_Semantic_Embedding). The development channel is the Hugging Face Hub tarball via manage_data.py (get/update). End users instead pull it anonymously from Cloudflare R2 with `semantics_manage.py pull`. Use when setting up or refreshing the semantic DB on a machine, or when deformalizations / vector stores are stale or missing. Do NOT run R2 `push` — it overwrites the shared remote and is a human-only action.
How to repackage/upload and download/unpack the Isabelle + paired AFP distribution, published together as contrib/Isabelle2025-2_and_afp-2026-05-13.tar.zst on the Hugging Face Hub (via manage_data.py update/get). Use when publishing a rebuilt Isabelle+AFP tarball (after isabelle scala_build, patches, etc.) or installing/refreshing the distribution on another machine.
How to read Isabelle introduction and elimination rules
Playbook for adding a new import to MathBench_ProverBase and reconciling any syntax conflicts (constant/type short-name resolution and notation) so that PutnamBench problems still parse to identical goal terms. Use when adding/changing imports of the MathBench_Prover session, or when investigating MathBench-vs-PutnamBench environment divergences.
How to read constants and theorems generated by Isabelle datatype and codatatype definitions
| name | isabelle-scala-plugin |
| description | How to write Isabelle/Scala plugins (Scala.Fun) that ML can call via PIDE protocol |
Isabelle's Scala.Fun mechanism lets ML code call Scala/JVM functions via the PIDE protocol. Use this when you need JVM capabilities from ML: file I/O, accessing PIDE session/document state, network, Java libraries, etc.
Working example: contrib/Semantic_Embedding/src/scala/pide_state.scala + contrib/Semantic_Embedding/Tools/pide_state.ML
Framework source: contrib/Isabelle2024/src/Pure/System/scala.scala
A Scala plugin requires a registered Isabelle component with these files:
etc/settings# -*- shell-script -*- :mode=shellscript:
MY_COMPONENT_HOME="$COMPONENT"
classpath "$MY_COMPONENT_HOME/lib/my_plugin.jar"
The classpath call is sufficient for both class loading AND service discovery (the jar's META-INF/isabelle/services is auto-scanned).
WARNING: Do NOT add isabelle_scala_service in settings — it causes jEdit startup crash (code 127).
etc/build.propstitle = Isabelle/Scala/My_Plugin
module = lib/my_plugin.jar
no_build = true
requirements = \
env:ISABELLE_SCALA_JAR
sources = \
src/scala/my_plugin.scala
services = \
my.package.My_Functions
CRITICAL: no_build = true is mandatory. Without it, jEdit crashes at startup with return code 127 (COMMAND NOT FOUND). You must manually run isabelle scala_build after temporarily removing this flag.
isabelle components -u /path/to/my/component
| Base class | Use when | ML calls with |
|---|---|---|
Scala.Fun + override invoke | Need Session object (document state, etc.) | Scala.function / Scala.function1 |
Scala.Fun_String | Simple string→string, no Session needed | Scala.function1 |
Scala.Fun_Bytes | Binary data | Scala.function1_bytes |
package isabelle.my_package
import isabelle._
object My_Function extends Scala.Fun("my_package.my_function", thread = true)
with Scala.Single_Fun
{
val here = Scala_Project.here
override def invoke(session: Session, args: List[Bytes]): List[Bytes] = {
val input = args.head.text // single string argument from ML
// ... do work ...
val body = XML.Encode.list(XML.Encode.string)(result_list)
List(Bytes(YXML.string_of_body(body)))
}
}
class My_Functions extends Scala.Functions(My_Function)
Key points:
thread = true: runs in a separate thread (required for session state access)Scala.Single_Fun: function takes/returns a single value (wrapped in List)val here = Scala_Project.here: required for error position reportingobject Echo extends Scala.Fun_String("my_package.echo") {
val here = Scala_Project.here
def apply(arg: String): String = arg.toUpperCase
}
Register all functions in one service class:
class My_Functions extends Scala.Functions(Function1, Function2, Function3)
val version = session.get_state().recent_finished.version.get_finished
val snapshot = session.get_state().snapshot()
// iterate all theory nodes
for ((name, node) <- version.nodes.iterator if name.is_theory) {
val file_path: String = name.node // e.g., "/path/to/Foo.thy"
val source: String = node.source // full source text
// iterate commands
for (command <- node.commands.iterator) {
val id: Long = command.id
val cmd_source: String = command.source
val line: Option[Int] = node.command_start_line(command) // 1-based
}
}
// resolve command ID + offset to file position (needs Snapshot)
snapshot.find_command_position(id, offset) // => Option[Line.Node_Position]
// Line.Node_Position has: .name (file), .line1 (1-based line), .column1 (1-based column)
(* string -> string *)
val result = Scala.function1 "my_package.my_function" input_string;
(* string list -> string list *)
val results = Scala.function "my_package.my_function" input_strings;
(* Bytes.T -> Bytes.T *)
val result = Scala.function1_bytes "my_package.my_function" input_bytes;
theory My_Theory imports Main
begin
ML_file \<open>Tools/my_tool.ML\<close>
end
Data interchange between ML and Scala uses YXML-encoded XML trees.
// primitives
XML.Encode.string : T[String]
XML.Encode.int : T[Int]
XML.Encode.long : T[Long]
XML.Encode.bool : T[Boolean]
// combinators
XML.Encode.pair(f, g) : T[(A, B)]
XML.Encode.triple(f,g,h) : T[(A, B, C)]
XML.Encode.list(f) : T[List[A]]
XML.Encode.option(f) : T[Option[A]]
// wrap to string
val yxml_string = YXML.string_of_body(XML.Encode.list(XML.Encode.string)(my_list))
(* primitives *)
XML.Decode.string : body -> string
XML.Decode.int : body -> int (* arbitrary precision, can decode Scala long *)
(* combinators *)
XML.Decode.pair f g : body -> 'a * 'b
XML.Decode.triple f g h : body -> 'a * 'b * 'c
XML.Decode.list f : body -> 'a list
XML.Decode.option f : body -> 'a option
(* parse from string *)
val decoded = YXML.parse_body yxml_string |> XML.Decode.list XML.Decode.string;
val body = XML.Encode.list (XML.Encode.pair XML.Encode.int XML.Encode.int) pairs;
val yxml_string = YXML.string_of_body body;
Scala decoding:
val pairs = XML.Decode.list(XML.Decode.pair(XML.Decode.int, XML.Decode.int))(
YXML.parse_body(input_string))
# 1. Temporarily enable building
sed -i '/^no_build/d' contrib/My_Component/etc/build.props
# 2. Build
isabelle scala_build
# 3. Restore no_build (CRITICAL for jEdit)
sed -i '/^module = lib/a no_build = true' contrib/My_Component/etc/build.props
| Changed | Action needed |
|---|---|
.scala file | Rebuild jar + restart jEdit |
.ML file only | Reload theory in jEdit (no rebuild) |
etc/settings | Restart jEdit |
etc/build.props | Rebuild jar + restart jEdit |
isabelle scala_build?This means the jar is already up-to-date (sources unchanged since last build). Verify with ls lib/*.jar.
no_build = true is mandatory in etc/build.props. Without it, jEdit startup crashes with Return code: 127 (COMMAND NOT FOUND).
Do NOT use isabelle_scala_service in etc/settings. It also causes the 127 crash. The classpath call alone is sufficient — services are discovered from the jar's META-INF/isabelle/services.
Position.T has no column field. It stores: line, offset (symbol offset), end_offset, label, file, id. Construct with Position.make0 line offset end_offset label file id or helpers like Position.line_file line file.
find_command_position returns 0-based positions. Use .line1 and .column1 for 1-based values matching Isabelle convention.
ML int is arbitrary precision (Poly/ML), so XML.Decode.int on the ML side can decode values encoded with XML.Encode.long on the Scala side.
recent_finished vs snapshot(): Use recent_finished.version.get_finished for read-only access to the document version. Use snapshot() when you need to resolve positions or access command state.
contrib/Isabelle2024/src/Pure/PIDE/document.scala)Document.Version — immutable snapshot of all theory nodes
.nodes: Nodes — graph of (Node.Name, Node) pairsDocument.Node — a theory file's content
.source: String — full source text.commands: Linear_Set[Command] — all commands in order.command_start_line(cmd): Option[Int] — 1-based line numberDocument.Node.Name — theory file identity
.node: String — file path.theory: String — theory name.is_theory: BooleanCommand — a single command (e.g., one lemma, one definition)
.id: Long — unique command ID.source: String — command source textSession databases at ~/.isabelle/Isabelle2024/heaps/<ML_ID>/log/<session>.db contain persisted PIDE markup including entity references for go-to-definition. See contrib/Semantic_Embedding/doc/goto_definition.md for full details.