Here is a small code demonstrating Java interoperability in Frege:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
We can observe the following things from the above code:
- Making use of a Java class and its methods
- Using a Java object in a Frege function
- Using Java Exceptions in functions
- Handling Java exceptions
1. Making use of a Java class and its methods:
If a Java class is pure then without much effort, we can use that class in Frege. For example,
1 2 3 4
A Java class is declared with
data declaration in Frege. The identifier after the
is the corresponding type for the Java class in Frege and the qualified Java class is identified after the
keyword followed by the instance methods, static methods or even some Frege functions not defined in the
original Java class.
An important point here is that the instance methods on BigInteger take Integer as their
first argument which is the
this reference on which the methods will be invoked.
Coming back to our original example, here
we are trying to use the mutable Java class
An obvious difference between this one and the
BigInteger example is that the
functions now do not have the
pure keyword in front.
The next difference is that the instance methods now cannot take the simple type like
LinkedList a as we did for
Integer but the type is now
Mutable s (LinkedList a) since it is not a pure function.
If we don’t annotate a native function
pure and we don’t use
Mutable to consume or return a mutable Object, it will be a
compilation error. Mutable objects can only be used in
IO actions so the return type
must be in ST or IO monad.
The LinkedList.add() method returns a boolean. Since
it is an impure function, it should be used in
ST monad. Here the boolean itself is pure so it is just
ST s Bool.
Take a look at the third function
LinkedList constructor. This function is impure and it returns
a mutable object, a new
LinkedList instance, so the return type is
ST s (Mutable s (LinkedList a)) for which the shorthand is
STMutable s (LinkedList a).
Here is an example for a native function not being part of a native
data declaration. This is useful when a native class is already
declared in Frege in some module but the function that we are looking
for is missing in the
showThrowable is the Frege function name for
Throwable.toString(). Since it is an
instance method on
Throwable, the first argument is of type
Throwable and then the formal arguments’ types (in this case, none) and return type.
2. Using a Java object in a Frege function
data declaration doesn’t have to just contain the native members, it can also have
additional Frege functions.
In our example, the function
fromFregeList is not defined in
the Java class but it has been added as an utility function to create a
LinkedList from a frege list.
Here again the same rule as in the previous section applies: To return a mutable Java object,
we should use
ST s (Mutable s TheJavaType) which is nothing but
STMutable s TheJavaType.
In the same way, the
plusTop function takes a mutable Java object so the parameter type is
Mutable s (LinkedList Int). Also since it consumes a mutable type, it must be in
ST monad hence
the return type is
ST s (Maybe Int) returning an
Maybe Int in
3. Using Java Exceptions in functions
To use a Java Exception class, it must be first defined in a Frege
module. It is the same as declaring native declaration for a Java class but
additionally we need to derive the
Exceptional type class so that the exception can later be handled with
1 2 3
The exceptions can then be used in native declarations as in
get function in our example:
4. Handling Java exceptions
In two ways, we can handle exceptions:
action `catch` handler1 `catch` handler2
The type of
Exceptional β => ST γ α -> (β->ST γ α) -> ST γ α.
actionis the code where an exception might be thrown and the handlers
handler2take an exception and return another value in
STmonad. The infix notation facilitates adding multiple handlers with better readability. Further here the
handler1must be more specific(in terms of the types of the exceptions being handled) than
handler2. Also note that from Frege standard library with respect to
Note If action is of the form:
doSomething argthen, depending on the strictness of
argmay be evaluated before the action is returned. Exceptions (i.e. undefined values) that occur in the construction of the action do not count as exceptions thrown during execution of it, and hence cannot be catched.
println (head ) `catch` ....
will not catch the exception that will be thrown when println evaluates
For a remedy, see
First, the type:
try :: Monad γ => (α-> γ β) -> α -> γ β
trytakes a function that produces a monadic value. If the function can throw an exception, it must result in an
STmonad which can then be passed to
catchto handle those exceptions. In our example,
\xs -> plusTop xs >>= (println . maybe "Got a null pointer" show)is the function which when applied to a
java.util.LinkedListmight throw a
1 2 3 4
Since the construction of action is deferred through a lambda
try eliminates the issue with
catch mentioned in the above note.
Extending a class or implementing an interface in Frege:
One thing that is not shown in the example is extending a Java class
or implementing an interface in Frege. Unfortunately both are not possible in Frege
yet. There is a workaround though using a Java class which extends a
class or implements an interface but instead of an implementation on
its own, it just delegates to a Frege function. For example, see
java.lang.Runnable in Frege using a Java class
frege.runtime.SwingSupport which takes a Frege function and then
delegates to it in
run method implementation.
This concludes our little experimentation calling Java from Frege. The other interesting side, calling Frege from Java, is for a future post.