object MLValue extends OperationCollection
- Source
- MLValue.scala
- Alphabetic
- By Inheritance
- MLValue
- OperationCollection
- AnyRef
- Any
- Hide All
- Show All
- Public
- Protected
Type Members
- abstract class Converter[A] extends AnyRef
An instance of this class describes the relationship between a Scala type
A
, the corresponding ML typea
, and the representation of values of typea
as exceptions in the object store.An instance of this class describes the relationship between a Scala type
A
, the corresponding ML typea
, and the representation of values of typea
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 aFuture[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 typeint -> data
. (data
is the ML analogue of Data, see the documentation of Data.) - Retrieve the value by invoking
retrieveInt
(gives aFuture[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 functionretrieve
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 typedata -> 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 functionretrieve
for simplicity here. Like above forstoreInt
, 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 likeimplicit 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 typesa1
anda2
(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
- Decide on an encoding of the integer
- class Ops extends AnyRef
- Attributes
- protected[mlvalue]
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 Ops(implicit isabelle: Isabelle, ec: ExecutionContext): Ops
Returns an instance of type Ops.
Returns an instance of type Ops. It is guaranteed that for each instance
isabelle
, exactly one instance ofObs
is created (using theec
from the first such invocation). (If you see this doc string in a class different from OperationCollection but no definition of the class Ops, treat this function as if it was private.)- Definition Classes
- OperationCollection
- def apply[A](value: A)(implicit conv: Converter[A], isabelle: Isabelle, executionContext: ExecutionContext): MLValue[A]
Creates an MLValue containing the value
value
.Creates an MLValue containing the value
value
. This transfersvalue
to the Isabelle process and stores it in the object store there.- returns
an MLValue that references the location in the object store
- Annotations
- @inline()
- 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 compileFunction[D1, D2, D3, D4, D5, D6, D7, R](ml: String)(implicit isabelle: Isabelle, ec: ExecutionContext, converter1: Converter[D1], converter2: Converter[D2], converter3: Converter[D3], converter4: Converter[D4], converter5: Converter[D5], converter6: Converter[D6], converter7: Converter[D7], converterR: Converter[R]): MLFunction7[D1, D2, D3, D4, D5, D6, D7, R]
Analogous to compileFunction[D1,D2,R], except for 7-tuples instead of 2-tuples.
- def compileFunction[D1, D2, D3, D4, D5, D6, R](ml: String)(implicit isabelle: Isabelle, ec: ExecutionContext, converter1: Converter[D1], converter2: Converter[D2], converter3: Converter[D3], converter4: Converter[D4], converter5: Converter[D5], converter6: Converter[D6], converterR: Converter[R]): MLFunction6[D1, D2, D3, D4, D5, D6, R]
Analogous to compileFunction[D1,D2,R], except for 6-tuples instead of 2-tuples.
- def compileFunction[D1, D2, D3, D4, D5, R](ml: String)(implicit isabelle: Isabelle, ec: ExecutionContext, converter1: Converter[D1], converter2: Converter[D2], converter3: Converter[D3], converter4: Converter[D4], converter5: Converter[D5], converterR: Converter[R]): MLFunction5[D1, D2, D3, D4, D5, R]
Analogous to compileFunction[D1,D2,R], except for 5-tuples instead of 2-tuples.
- def compileFunction[D1, D2, D3, D4, R](ml: String)(implicit isabelle: Isabelle, ec: ExecutionContext, converter1: Converter[D1], converter2: Converter[D2], converter3: Converter[D3], converter4: Converter[D4], converterR: Converter[R]): MLFunction4[D1, D2, D3, D4, R]
Analogous to compileFunction[D1,D2,R], except for 4-tuples instead of 2-tuples.
- def compileFunction[D1, D2, D3, R](ml: String)(implicit isabelle: Isabelle, ec: ExecutionContext, converter1: Converter[D1], converter2: Converter[D2], converter3: Converter[D3], converterR: Converter[R]): MLFunction3[D1, D2, D3, R]
Analogous to compileFunction[D1,D2,R], except for 3-tuples instead of 2-tuples.
- def compileFunction[D1, D2, R](ml: String)(implicit isabelle: Isabelle, ec: ExecutionContext, converter1: Converter[D1], converter2: Converter[D2], converterR: Converter[R]): MLFunction2[D1, D2, R]
Like compileFunction[D,R], except that the ML code
ml
must be a function of typed1 * d2 -> r
whered1,d2,r
are the ML types corresponding toD1,D2,R
.Like compileFunction[D,R], except that the ML code
ml
must be a function of typed1 * d2 -> r
whered1,d2,r
are the ML types corresponding toD1,D2,R
. The resulting MLFunction2f
can then be invoked also asf(x1,x2)
and not only asf((x1,x2))
(as would be the case if we had used compileFunction[D,R][(D1,D2),R](ml)
to compile the function). - def compileFunction[D, R](ml: String)(implicit isabelle: Isabelle, ec: ExecutionContext, converterD: Converter[D], converterR: Converter[R]): MLFunction[D, R]
Compiles an ML function and inserts it into the object store.
Compiles an ML function and inserts it into the object store.
ml
must compile to an ML value of typed -> r
whered,r
are the ML type corresponding toD,R
(as specified by the implicit Converters). Then the result is converted to an exception and stored in the object store. An MLFunction referencing that object is returned. (An MLFunction is an MLValue with some extra methods for evaluating functions.)For functions with more than one argument, see also compileFunction[D1,D2,R], compileFunction[D1,D2,D3,R], etc.
- def compileFunction0[R](ml: String)(implicit isabelle: Isabelle, ec: ExecutionContext, converter: Converter[R]): MLFunction0[R]
Like compileFunction[D,R], except that the ML code
ml
must be a function of typeunit -> r
wherer
is the ML type corresponding toR
.Like compileFunction[D,R], except that the ML code
ml
must be a function of typeunit -> r
wherer
is the ML type corresponding toR
. The resulting MLFunction2f
can then be invoked also asf()
and not only asf(())
(as would be the case if we had used compileFunction[D,R][Unit,R](ml)
to compile the function). - def compileValue[A](ml: String)(implicit isabelle: Isabelle, ec: ExecutionContext, converter: Converter[A]): MLValue[A]
Compiles ML code
ml
and inserts it into the object store.Compiles ML code
ml
and inserts it into the object store.ml
must compile to an ML value of typea
wherea
is the ML type corresponding toA
(as specified by the implicit Converter). Then the result is converted to an exception and stored in the object store. An MLValue referencing that object is returned.If
ml
is an ML function, compileFunction below might be more convenient. - def compileValueRaw[A](ml: String)(implicit isabelle: Isabelle, ec: ExecutionContext): MLValue[A]
Compiles
ml
code and inserts it into the object store (without any conversion).Compiles
ml
code and inserts it into the object store (without any conversion).ml
must compile to an ML value of typeexn
that is the encoding of some Scala value of typeA
. (As specified by the Converter forA
, even though that converter is not actually used by this function.)The function does not check whether the encoding is correct for the type
A
! (And if that is not the case, the returned MLValue breaks type-safety.)In most situations, it is preferrable to use higher level compilation functions such as compileValue or compileFunction or MLStoreFunction or MLRetrieveFunction that take care of the encoding as exceptions automatically.
- final def eq(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef
- def equals(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef → Any
- final def getClass(): Class[_ <: AnyRef]
- Definition Classes
- AnyRef → Any
- Annotations
- @native() @HotSpotIntrinsicCandidate()
- def hashCode(): Int
- Definition Classes
- AnyRef → Any
- Annotations
- @native() @HotSpotIntrinsicCandidate()
- def init()(implicit isabelle: Isabelle, executionContext: ExecutionContext): Unit
Makes sure an Ops instance for the instance
isabelle
is initialized.Makes sure an Ops instance for the instance
isabelle
is initialized. This is useful when code needs to be sure that the global initialization inside the Ops class has happened (e.g., declarations of ML types via Isabelle.executeMLCodeNow) even if it does not access any of the fields in the Ops class.Can safely be called several times with the same
isabelle
and/orexecutionContext
.- Definition Classes
- OperationCollection
- final def isInstanceOf[T0]: Boolean
- Definition Classes
- Any
- def matchFailData(name: String): String
Utlity method for generating ML code.
Utlity method for generating ML code. Analogous to matchFailExn, but for cases when we pattern match a value of type
data
.- Annotations
- @inline()
- def matchFailExn(name: String): String
Utility method for generating ML code.
Utility method for generating ML code. It returns an ML fragment that can be used as the fallback case when pattern matching exceptions, the fragment raises an error with a description of the exception.
Example: Instead of ML code
"fn E_Int i => i"
, we can writes"fn E_Int i => i | ${matchFailExn("my function")}"
to get more informative error messages on pattern match failures.- name
A short description of the purpose of the match/ML function that is being written. Will be included in the error message.
- Annotations
- @inline()
- final def ne(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef
- def newOps(implicit isabelle: Isabelle, ec: ExecutionContext): Ops
Should construct an instance of type Ops
Should construct an instance of type Ops
- Attributes
- protected
- Definition Classes
- MLValue → OperationCollection
- final def notify(): Unit
- Definition Classes
- AnyRef
- Annotations
- @native() @HotSpotIntrinsicCandidate()
- final def notifyAll(): Unit
- Definition Classes
- AnyRef
- Annotations
- @native() @HotSpotIntrinsicCandidate()
- def removeFuture[A](future: Future[MLValue[A]])(implicit ec: ExecutionContext): MLValue[A]
- final def synchronized[T0](arg0: => T0): T0
- Definition Classes
- AnyRef
- def toString(): String
- Definition Classes
- AnyRef → Any
- def unsafeFromId[A](id: ID): MLValue[A]
Same as unsafeFromId(Future[ID]), except that
id
is given directly and not as a Future. - def unsafeFromId[A](id: Future[ID]): MLValue[A]
Unsafe operation for creating an MLValue.
- 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])