Packages

  • package root
    Definition Classes
    root
  • package de
    Definition Classes
    root
  • package unruh
    Definition Classes
    de
  • package isabelle

    This library allows to control an Isabelle process from a Scala application.

    This library allows to control an Isabelle process from a Scala application. For first steps and pointers, see the README.

    In case of missing/incorrect documentation, create an issue.

    Definition Classes
    unruh
  • package mlvalue

    This package contains classes for type safe manipulation of values stored in a running Isabelle process (managed by an instance of control.Isabelle).

    This package contains classes for type safe manipulation of values stored in a running Isabelle process (managed by an instance of control.Isabelle). See MLValue for an introduction.

    Definition Classes
    isabelle
  • object MLValue extends OperationCollection
    Definition Classes
    mlvalue
  • Converter
  • Ops

abstract class Converter[A] extends AnyRef

An instance of this class describes the relationship between a Scala type A, the corresponding ML type a, and the representation of values of type a as exceptions in the object store. To support new types, a corresponding Converter object/class needs to be declared.

We explain how a converter works using the example of IntConverter.

The first step is to decide which Scala type and which ML type should be related. In this case, we choose scala.Int on the Scala side, and int on the ML side. We declare the correspondence using the mlType method:

final object IntConverter extends MLValue.Converter[A] {
  override def mlType = "int"
  ...
}

Next, we have to decide how decide how code is converted from an int to an exception (so that it can be stored in the object store). In this simple case, we first declare a new exception for holding integers:

isabelle.executeMLCodeNow("exception E_Int of int")

This should be done globally (once per Isabelle instance). Declaring two (even identical) exceptions with the same name E_Int must be avoided! See OperationCollection for utilities how to manage this. (E_Int specifically does not need to be declared since it is predeclared.)

We define the method valueToExn that returns the ML source code to convert an ML value of type int to an exception:

final object IntConverter extends MLValue.Converter[A] {
    ...
    override def valueToExn(implicit isabelle: Isabelle, ec: ExecutionContext): String = "fn x => E_Int x"  // or equivalently: = "E_Int"
    ...
  }

We also need to convert in the opposite direction:

final object IntConverter extends MLValue.Converter[A] {
  ...
  override def exnToValue(implicit isabelle: Isabelle, ec: ExecutionContext): String = "fn (E_Int x) => x"
  ...
}

(Best add some meaningful exception in case of match failure, e.g., using boilerplate from MLValue.matchFailExn. Omitted for clarity in this example.)

Next, we need to write a function for retrieving integer values from the object store (method retrieve). It gets a value : MLValue[Int] as input and has to return a Future[Int] containing the stored integer. In principle, there are not restrictions how this is done but the simplest way is the following approach:

  • Decide on an encoding of the integer i as a tree in the Data data structure. (In this case, simply DInt(i) will do the trick.)
  • Define an MLRetrieveFunction retrieveInt that performs this encoding on the ML side, i.e., we need to write ML code for a function of type int -> data. (data is the ML analogue of Data, see the documentation of Data.)
  • Retrieve the value by invoking retrieveInt (gives a Future[Data])
  • Convert the resulting Data to an Int. That is:
final object IntConverter extends MLValue.Converter[A] {
  ...
  override def retrieve(value: MLValue[Int])
                       (implicit isabelle: Isabelle, ec: ExecutionContext): Future[Int] = {
     val retrieveInt = MLRetrieveFunction[Int]("fn i => DInt i")   // compile retrieveInt
     for (data <- retrieveInt(value.id); // invoke retrieveInt to transfer from Isabelle
          DInt(long) = data)             // decode the data (simple pattern match)
       yield long.toInt   // return the integer
  }
  ...
}

Note that val retrieveInt = ... was written inside the function retrieve for simplicity here. However, since it invokes the ML compiler, it should be invoked only once (per Isabelle instance, like the executeMLCodeNow above). See OperationCollection for an auxiliary class helping to manage this.

Finally, we also need a function store that transfers an integer into the Isabelle object store. Again, the easiest way is to use the following steps:

  • Define an encoding of integers as Data trees (we use the same encoding as before)
  • Define an MLStoreFunction storeInt that decodes the data back to an int on the ML side, i.e., we need to write ML code for a function of type data -> int.
  • Encode the integer to be stored as Data
  • Invoke storeInt to transfer the Data to ML and store the integer in the object store. That is:
final object IntConverter extends MLValue.Converter[A] {
  ...
  override def store(value: Int)(implicit isabelle: Isabelle, ec: ExecutionContext): MLValue[Int] = {
    val storeInt = MLStoreFunction[Int]("fn DInt i => i")   // compile storeInt
    val data = DInt(value)       // encode the integer as Data
    Ops.storeInt(data)           // invoke storeInt to get the MLValue
  }
}

Note that val retrieveInt = ... was written inside the function retrieve for simplicity here. Like above for storeInt, this should be done only once (per Isabelle instance).

This concludes the definition of the Converter. Finally, the converter should be made available as an implicit value. That is, we define in a suitable place

implicit val intConverter = IntConverter

so that intConverter can be imported as an implicit where needed. (And if the converter we constructed is not an object but a class taking other converters as arguments, we instead write something like

implicit def listConverter[A](implicit converter: Converter[A]): ListConverter[A] = new ListConverter()(converter)

or similar.)

Notes

  • Several Scala types can correspond to the same ML type (e.g., Int and Long both correspond to int).
  • If the converters for two Scala types A,B additionally have the same encoding as exceptions (defined via valueToExn, exnToValue in their Converters), then MLValue[A] and MLValue[B] can be safely typecast into each other.
  • It is not recommended to have the same Scala type A for two different ML types a1 and a2 (or for the same ML type but with different encoding). First, this would mean that there have to be two different instances of Converter[A] available (which means Scala cannot automatically choose the right one). Second, it means that one has to be manually keep track which Converter was used for which value (no type safety).
  • The attentive reader will notice that we use MLRetrieveFunction.apply and MLStoreFunction.apply when defining the converter, but that these functions take the converter we are currently defining as an implicit argument! However, this cyclic dependency is not a problem because MLRetrieveFunction.apply and MLStoreFunction.apply never invoke store and retrieve from the converter, so we can call those apply functions from store and retrieve.
  • In simple cases (when A is simply supposed to be a wrapper around a reference to a value in the Isabelle process, constructions of converters are simplified by using MLValueWrapper or AdHocConverter.
A

the Scala type for which a corresponding ML type is declared

Source
MLValue.scala
Linear Supertypes
Ordering
  1. Alphabetic
  2. By Inheritance
Inherited
  1. Converter
  2. AnyRef
  3. Any
  1. Hide All
  2. Show All
Visibility
  1. Public
  2. Protected

Instance Constructors

  1. new Converter()

Abstract Value Members

  1. abstract def exnToValue(implicit isabelle: Isabelle, ec: ExecutionContext): String

    Returns ML code for an (anonymous) function of type exn -> a that converts a value encoded as an exception back into the original value.

    Returns ML code for an (anonymous) function of type exn -> a that converts a value encoded as an exception back into the original value.

    It is recommended that this function produces informative match failures in case of invalid inputs. MLValue.matchFailExn is a helper function that facilitates this.

    This function should always return the same value, at least for the same isabelle.

  2. abstract def mlType(implicit isabelle: Isabelle, ec: ExecutionContext): String

    Returns the ML type corresponding to A.

    Returns the ML type corresponding to A.

    This function should always return the same value, at least for the same isabelle.

  3. abstract def retrieve(value: MLValue[A])(implicit isabelle: Isabelle, ec: ExecutionContext): Future[A]

    Given an mlvalue.MLValue value, retrieves and returns the value referenced by value in the Isabelle object store.

    Given an mlvalue.MLValue value, retrieves and returns the value referenced by value in the Isabelle object store.

    Must not invoke value.retrieve or value.retrieveNow because those functions invoke this.retrieve. (But calling retrieve or retrieveNow on other MLValues is allowed as long as no cyclic dependencies are created.)

  4. abstract def store(value: A)(implicit isabelle: Isabelle, ec: ExecutionContext): MLValue[A]

    Given a value : A, transfers and stores value in the Isabelle object store and returns an mlvalue.MLValue referencing the value in the object store.

    Given a value : A, transfers and stores value in the Isabelle object store and returns an mlvalue.MLValue referencing the value in the object store.

    Must not invoke MLValue(value) because that functions invokes this.store. (But calling MLValue(...) on other values is allowed as long as no cyclic dependencies are created.)

  5. abstract def valueToExn(implicit isabelle: Isabelle, ec: ExecutionContext): String

    Returns ML code for an (anonymous) function of type a -> exn that converts a value into its encoding as an exception.

    Returns ML code for an (anonymous) function of type a -> exn that converts a value into its encoding as an exception.

    It is recommended that this function produces informative match failures in case of invalid inputs. MLValue.matchFailExn is a helper function that facilitates this.

    This function should always return the same value, at least for the same isabelle.

Concrete Value Members

  1. final def !=(arg0: Any): Boolean
    Definition Classes
    AnyRef → Any
  2. final def ##: Int
    Definition Classes
    AnyRef → Any
  3. final def ==(arg0: Any): Boolean
    Definition Classes
    AnyRef → Any
  4. final def asInstanceOf[T0]: T0
    Definition Classes
    Any
  5. def clone(): AnyRef
    Attributes
    protected[lang]
    Definition Classes
    AnyRef
    Annotations
    @throws(classOf[java.lang.CloneNotSupportedException]) @native() @HotSpotIntrinsicCandidate()
  6. final def eq(arg0: AnyRef): Boolean
    Definition Classes
    AnyRef
  7. def equals(arg0: AnyRef): Boolean
    Definition Classes
    AnyRef → Any
  8. final def getClass(): Class[_ <: AnyRef]
    Definition Classes
    AnyRef → Any
    Annotations
    @native() @HotSpotIntrinsicCandidate()
  9. def hashCode(): Int
    Definition Classes
    AnyRef → Any
    Annotations
    @native() @HotSpotIntrinsicCandidate()
  10. final def isInstanceOf[T0]: Boolean
    Definition Classes
    Any
  11. final def ne(arg0: AnyRef): Boolean
    Definition Classes
    AnyRef
  12. final def notify(): Unit
    Definition Classes
    AnyRef
    Annotations
    @native() @HotSpotIntrinsicCandidate()
  13. final def notifyAll(): Unit
    Definition Classes
    AnyRef
    Annotations
    @native() @HotSpotIntrinsicCandidate()
  14. final def synchronized[T0](arg0: => T0): T0
    Definition Classes
    AnyRef
  15. def toString(): String
    Definition Classes
    AnyRef → Any
  16. final def wait(arg0: Long, arg1: Int): Unit
    Definition Classes
    AnyRef
    Annotations
    @throws(classOf[java.lang.InterruptedException])
  17. final def wait(arg0: Long): Unit
    Definition Classes
    AnyRef
    Annotations
    @throws(classOf[java.lang.InterruptedException]) @native()
  18. final def wait(): Unit
    Definition Classes
    AnyRef
    Annotations
    @throws(classOf[java.lang.InterruptedException])

Deprecated Value Members

  1. def finalize(): Unit
    Attributes
    protected[lang]
    Definition Classes
    AnyRef
    Annotations
    @throws(classOf[java.lang.Throwable]) @Deprecated
    Deprecated

Inherited from AnyRef

Inherited from Any

Ungrouped