ワンクリックで
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.