Yona allows interoperability with other languages on GraalVM through the Polyglot APIs.
First form of interoperability with other scripting languages on GraalVM is supported by function
eval. This function takes two arguments, first is the symbol of a language, such as
:python and the second argument is a string of the expression to be evaluated in this language.
eval :yona "raise :test \"test msg\""
Interoperability with Java is an important feature in Yona and allows calling Java code directly from Yona and vice-versa.
In order to call Yona code from Java, one must build a GraalVM Polyglot context and then evaluate a Yona source using this context:
import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Value; Context context = Context.newBuilder().build(); Value returnValue = context.eval("yona", "5 + 3");
Type-conversions, constructing objects and calling methods¶
Yona API for calling code in Java consists of two modules:
Java- for instantiating new objects, checking instance type of an object, raising Java exceptions and casting Java objects
java\Types- for converting Yona types to Java types (for example Yona contains only 64bit integers, so they need to be converted to Java integers when calling a Java method that expects an integer. Same for double vs float.
Simple example to create a BigInteger in Yona and then check that it is actually of
let type = Java::type "java.math.BigInteger" instance = Java::new type ["50"] in Java::instanceof instance type
Yona has only one integer type (which is actually Java long) and only one float type (Java double). Yona cannot safely map these into Java types automatically. As Yona is dynamically typed, there is simply no way for Yona to know that you're trying to call a Java function expecting an integer and not a long for example.
Therefore such automatic conversions do not really happen, and when calling a Java function, expecting a Java integer, you must manually convert this using
Java\Types::to_float respectively. More details in the documentation for that module.
On the opposite side of the contract - returning Java values to Yona, following conversions are applied:
nullis represented as
- Java array is represented as a sequence
int(32-bit) is represented as a Yona
float(32-bit) is represented as a Yona
CharSequenceis represented as Yona string
char(UTF-16) is represented as Yona
- all other types which are shared by both Java and Yona, such as
double, those are kept as they are
- otherwise objects are wrapped as Yona native objects
Calling methods on an object¶
Yona allows using module call operator
:: to be used to call object methods as well. This is shown in the following example for multiplying two
let type = Java::type "java.math.BigInteger" big_two = Java::new type ["2"] big_three = Java::new type ["3"] result = big_two::multiply big_three # calling a method `multiply` on object of type BigInteger. in result::longValue
Calling static methods in Java¶
Static methods may be called just as easily from Yona:
(java\util\Collections::singletonList 5)::get (java\Types::to_int 0)
Java static methods can be easily called from Yona, as if it were a Yona module/function.
Handling Java exceptions¶
Raising a Java exception from Yona is possible thanks to function
try let type = Java::type "java.lang.ArithmeticException" error = Java::new type ["testing error"] in Java::throw error catch (:java, error_msg, _) -> error_msg # "java.lang.ArithmeticException: testing error" will be the result end
The example above shows how to throw and catch Java exceptions in Yona code.
Limitations of Polyglot APIs and other notes¶
- Java interop is implemented via Java reflection. This may be an issue when using native image built version of Yona interpreter.
- Currying on Java static function/method calls works with the same semantics as for Yona functions. Therefore, one may create a curried Java method simply by not passing all the necessary arguments to method calls.
- Java methods are resolved by names only. Since Yona is a dynamically typed language, Yona simply doesn't have the knowledge to determine the correct method otherwise. It cannot even utilize the number of arguments, because the method call may be curried. Therefore it picks the first method of the given name and tries to use that.
- Exceptions related to Polyglot APIs in Yona have a symbol of