class Isabelle extends AnyRef
A running instance of Isabelle.
The Isabelle process is initialized with some ML code that allows this class to remote control Isabelle. In more detail:
The Isabelle process maintains a map from IDs to values (the "object store"). Those values can be of any type (e.g., integers, terms, functions, etc.). (How this works in a strongly typed language such as ML is described below.) The Isabelle class has functions to operate on the values in the object store (e.g., creating new objects, retrieving the value of an object, performing operations on objects).
The operations provided by this class are very lowlevel. For more convenient and type-safe operations on values in the object store, see de.unruh.isabelle.mlvalue.MLValue.
Operations on objects are asynchronous and return futures.
On the Scala side, the IDs of objects are represented by the class de.unruh.isabelle.control.Isabelle.ID. These IDs ensure garbage collection – if an ID is not referenced any more on the Scala side, it will be removed from the object store in the Isabelle process, too.
To be able to store objects of different types in the object store, even though ML does not support subtyping,
we make use of the fact that all exceptions share the same ML type exn
. The object store stores only values of
type exn
. To store, e.g., integers, we define an exception exception E_Int of int
. Then for an integer i
,
E_Int i
is an exception that can be stored in the object store. We can convert the exception back to an integer
using the function fn E_Int i => i
that uses pattern matching. (This will raise Match
if the given exception is
does not contain an integer. This way we achieve dynamic typing of our object store.) The following exceptions are
predefined in structure Control_Isabelle
:
exception E_Function of exn -> exn exception E_Int of int exception E_String of string exception E_Pair of exn * exn
(That structure also exports functions store
and handleLines
which are for internal use only
and must not be used in the ML code.)
Note that some of the exception again refer to the exn type, e.g., E_Pair
. Thus, to store a pair of integers,
we use the term E_Pair (E_Int 1, E_Int 2)
.
New exceptions for storing other types can be defined at runtime using executeMLCode.
- Source
- Isabelle.scala
- Alphabetic
- By Inheritance
- Isabelle
- AnyRef
- Any
- Hide All
- Show All
- Public
- Protected
Instance Constructors
- new Isabelle(setup: Setup, build: Boolean = true)
- setup
Configuration object that specifies the path of the Isabelle binary etc. See de.unruh.isabelle.control.Isabelle.Setup. This also specifies with Isabelle heap to load.
- build
Whether to build the Isabelle heap before running Isabelle. If false, the heap will never be built. (This means changes in the Isabelle theories will not be reflected. And if the heap was never built, the Isabelle process fails.) If true, the Isabelle build command will be invoked. That command automatically checks for changed dependencies but may add a noticable delay even if the heap was already built.
Value Members
- final def !=(arg0: Any): Boolean
- Definition Classes
- AnyRef → Any
- final def ##: Int
- Definition Classes
- AnyRef → Any
- final def ==(arg0: Any): Boolean
- Definition Classes
- AnyRef → Any
- def applyFunction(f: Future[ID], x: Data)(implicit ec: ExecutionContext): Future[Data]
Like applyFunction(ID,Data), except
f
is a future. - def applyFunction(f: ID, x: Data): Future[Data]
Applies
f
tox
and returns the result.Applies
f
tox
and returns the result.f
must be the ID of an object in the object store of the formE_Function f'
(and thusf'
of ML typedata -> data
).x
is serialized and transferred to the Isabelle process, the valuef' x
is computed, serialized and transferred back.By definition of the type Isabelle.Data,
x
can be a tree containing integers, strings, and object IDs. When transferring an object ID to the Isabelle process, it is replaced by the object (exception) that is referred by the ID. And similarly, objects (exceptions) in the return value are added to the object store and replaced by IDs upon transfer to the Scala side.This behavior gives rise to two simple use patterns:
Retrieving values: Say
tree
is some algebraic data type on the ML side,Tree
is a corresponding Scala class,encode : tree -> data
is a function that encodes a tree asdata
(using the DList, DInt, and DString constructors only), andE_Tree of tree
is an exception type to store trees in the object store. Then we can define a function for retrieving a tree from the object store to Scala as follows:val encodeID : Future[ID] = isabelle.storeValue("fn DObject (E_Tree tree) => encode tree") def decode(data: Data) : Tree = ??? // The opposite of the ML function encode def retrieve(id: ID) : Tree = { // Apply encode to the element referenced by id, result is an encoding of the tree as Data val dataFuture : Future[Data] = isabelle.applyFunction(encodeID, DObject(id)) // For simplicitly, we force synchronous execution val data : Data = Await.result(dataFuture, Duration.Inf) decode(data) }
Storing values: Continuing the above example, say
decode : data -> tree
is an ML function that decodes trees (inverse ofencode
above). Then we can store trees in the object store from Scala using the following functionstore
:val decodeID : Future[ID] = isabelle.storeValue("fn data => DObject (E_Tree (decode data))") def encode(tree: Tree) : Data = ??? // The opposite of the ML function decode def store(tree: Tree) : ID = { // Apply ML-decode to the Scala-encoded tree, store it in the object store, and return the ID (inside a Data) val dataFuture : Future[Data] = isabelle.applyFunction(decodeID, encode(tree)) // For simplicitly, we force synchronous execution val data : Data = Await.result(dataFuture, Duration.Inf) // get the ID inside the returned data (referring to the tree object in the object store) val DObject(id) = data id }
Of course, arbitrary combinations of these two ideas are possible. For example, one could have a Scala data structure that contains IDs of objects still on the ML side. These data structures can be serialized and deserialized similar to the above example, using the fact that the type Isabelle.Data allows IDs to occur anywhere in the tree.
Objects added to the object store by this mechanism are garbage collected on the ML side when the corresponding IDs are not used any more on the Scala side.
This approach is very low level. In particular, there is no type system support to ensure that the IDs contained in the serialized data actually refer to objects of the right type. A higher level typesafe approach for accessing data in the object store is given by de.unruh.isabelle.mlvalue.MLValue (see there). However,
MLValue
s internally use the mechanism described here to transfer data to/from the Isabelle process. Thus, to add support forMLValue
s of new types, theapplyFunction
needs to be used.- returns
A future holding the ID of the result (or holding an exception if the
f
is notE_Function f'
orf' x
throws an exception in ML)
- See also
Isabelle.Data for information what kind of data can be contained in
x
and the result
- final def asInstanceOf[T0]: T0
- Definition Classes
- Any
- def clone(): AnyRef
- Attributes
- protected[lang]
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.CloneNotSupportedException]) @native() @HotSpotIntrinsicCandidate()
- def destroy(): Unit
Kills the running Isabelle process.
Kills the running Isabelle process. After this, no more operations on values in the object store are possible. Futures corresponding to already running computations will throw an IsabelleDestroyedException.
- final def eq(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef
- def equals(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef → Any
- def executeMLCode(ml: String): Future[Unit]
Executes the ML code
ml
in the Isabelle process.Executes the ML code
ml
in the Isabelle process.WARNING: This has a global effect on the Isabelle process because it modifies the ML name space.
Definitions made in
ml
affect the global Isabelle name space. This is intended mostly for defining new types. To create values (e.g., ifml
is the code of a function that should be executed), preferably use storeValue which creates anonymous values in the object store. The ML code is executed in a context where the structureControl_Isabelle
is not opened (i.e., you have to writeControl_Isabelle.E_Int
instead ofE_Int
).- returns
A future that completes when the code was executed. (Or throws an IsabelleControllerException if the ML code compilation/execution fails.)
- def executeMLCodeNow(ml: String): Unit
Like executeMLCode but waits for the code to be executed before returning.
- final def getClass(): Class[_ <: AnyRef]
- Definition Classes
- AnyRef → Any
- Annotations
- @native() @HotSpotIntrinsicCandidate()
- def hashCode(): Int
- Definition Classes
- AnyRef → Any
- Annotations
- @native() @HotSpotIntrinsicCandidate()
- def isDestroyed: Boolean
Returns whether the Isabelle process has been destroyed (via destroy)
- final def isInstanceOf[T0]: Boolean
- Definition Classes
- Any
- final def ne(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef
- final def notify(): Unit
- Definition Classes
- AnyRef
- Annotations
- @native() @HotSpotIntrinsicCandidate()
- final def notifyAll(): Unit
- Definition Classes
- AnyRef
- Annotations
- @native() @HotSpotIntrinsicCandidate()
- val setup: Setup
- def storeValue(ml: String): Future[ID]
Executes the ML expression
ml
in the Isabelle process.Executes the ML expression
ml
in the Isabelle process.WARNING: This has a global effect on the Isabelle process because it modifies the ML name space.
The expression must be of ML type
exn
. The result of evaluating the expression is added to the object store. The ML code is executed in a context where the structureControl_Isabelle
is opened (i.e., you can writeE_Int
instead ofControl_Isabelle.E_Int
).Example:
storeValue("exception E_Term of term")
(this is actually done by de.unruh.isabelle.pure.Term).In code that is supposed to support multiple instances of Isabelle, it can be cumbersome to keep track in which instances a given ML code fragment was already executed. See OperationCollection for a helper class to facilitate that.
- returns
Future that contains an ID referencing the result in the object store. (Or throws an IsabelleControllerException if the ML code compilation/execution fails.)
- final def synchronized[T0](arg0: => T0): T0
- Definition Classes
- AnyRef
- def toString(): String
- Definition Classes
- AnyRef → Any
- final def wait(arg0: Long, arg1: Int): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.InterruptedException])
- final def wait(arg0: Long): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.InterruptedException]) @native()
- final def wait(): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.InterruptedException])