Skip to content

Calling Frege Code from Java

Ingo Wechsung edited this page Oct 6, 2013 · 22 revisions

It is easy to use Java classes and methods from Frege, however, it is also possible the other way. This guide provides some documentation, tips and tricks.

Basics of the Frege Run Time System

The Frege Run Time System (see also this drawing) consists of a number of mostly abstract classes and interfaces, and a handful of static methods that implement some primitives needed in Frege code. The Javadocs for package frege.runtime can be found here.

The run time system is stateless, i.e. one does not need to initialize or setup anything.

The main concern of the run time system is to provide for lazy evaluation and partial function application.

Lazy Evaluation

Results from function calls as well as components of Frege values can be lazy. The Java type of such values will be Lazy or Object, where Lazy will evaluate to some algebraic data type or function type, while Object may be a lazy native (i.e. Java) value or a type represented by a type variable.

For example, while tuples of different types can be distinguished by the Frege compiler, the information about type arguments gets lost in Java, and there is only one class (TTuple2) that is used for all tuples. Hence, both members of a tuple have the Java type Object, and this is also the return type of functions fst and snd. The same is true for list elements: because in the most general case we do not know anything about them, their type is Object.

To evaluate a lazy value it with Java type Lazy that will evaluate to TMaybe, write:

it.<TMaybe>forced()

To evaluate a lazy value it with Java type Object that will evaluate to a string, write:

Delayed.<String>forced(it)

This works no matter if the value is actually lazy or not. However, it's the final type that matters, and one gets a class cast exception if the evaluated value has an incompatible type.

It is never necessary to do the conversion in the other direction, because:

  • every non-native Frege value, algebraic or functional, implements interface Lazy
  • every value, even a primitive one, is silently converted or casted to Object by Java

Working with function values

Every non lazy function value will have type Lambda. Again, further type information like argument and return types are lost in translation to Java.

The only interesting thing one can do with a Lambda is to apply it to an argument:

Lambda fun = ......
Applicable inter = fun.apply(42);    

All we know about the result of apply is that it could be another Lambda or, if all arguments have been supplied, that it is a Lazy value. This is encapsulated in interface Applicable, which offers only two methods: apply(Object), which results in yet another Applicable and result(), which tells that we are done with applying arguments and want to have the Lazy result.

The following code assumes that our original function was of type Int -> Int -> (String, Int) and shows how to get at the String value.

String s = Delayed.<String>forced(
        inter.apply(43)   // supply second argument
             .result()    // get the `Lazy` value
             .<TTuple2>forced()  // evaluate result
             .mem1);             // get the first component

Tips and Tricks

  • Know the types of the Frege functions you are going to call.
  • Restrict yourself to a handful of functions.
  • If possible, specialize functions with polymorphic types as much as possible.
  • If unsure as of how to call a certain function, write a small Frege program that does just that, compile it and study the resulting java code.
  • It is safe to assume defensively that a function takes all arguments strict, but returns a lazy result. This way, as long as the Frege type signature of the function remains the same, your Java code that calls the function will work regardless of the implementation of the function.

Don'ts

  • Never pass null into Frege code. There are no exceptions to this rule.
  • Just because a function argument is of type Object doesn't mean you can pass anything.

Mapping of Frege Items

Packages/Modules

A Frege module is compiled to a Java class that acts as namespace for the items defined in the module.

module tutorial.Example where
import frege.prelude.Floating

quadrt :: Double -> Double
quadrt = sqrt . sqrt

Here is an outline of the corresponding Java code (comments introduced manually):

package tutorial;    // missing if Frege module name is a simple one

import frege.prelude.PreludeList;  // Import of Frege modules
import frege.prelude.Arrays;       // you didn't know they existed ...
import frege.Prelude;
import frege.prelude.Floating;     // ... and also explicitly imported ones.
import frege.prelude.Maybe;
import frege.prelude.PreludeBase;
import frege.prelude.PreludeNative;
import frege.prelude.PreludeMonad;
import frege.prelude.PreludeText;

@SuppressWarnings("unused")    // We'll have lots of unused local vars. Sorry.
@frege.runtime.Meta.FregePackage(       // Meta information used when this
                                        // package is ever imported.
   source="/home/.../Example.fr", time=1357511227564L,
   ops={
        @frege.runtime.Meta.Operator(name="<$>", kind=0, prec=13),
   // ... and so on and on ....
)
final public class Example {            // the module namespace
    final public static double quadrt(final double arg$1) {
        return java.lang.Math.sqrt(java.lang.Math.sqrt(arg$1));
    }
}

As one can see in the previous example, a top level function is translated to a public static method that is a member of the module namespace class. But it is, unfortunately, not always that easy, see below.

Types

Types appear as static classes or interfaces that are members of the module class. Their names always starts with a capital T followed by the original name.

Enumeration Types

An enumeration type is one that has only nullary constructors:

data Color = Red | Green | Blue 
    where
        favored Blue = true
        favored _    = false

This compiles to:

final public static class TColor  {
  private TColor() {}
  final public static short Blue = 2;
  final public static short Green = 1;
  final public static short Red = 0;

  final public static boolean favored(final short arg$1) {
    if (arg$1 == TColor.Blue) {
      return true;
    }
    return false;
  }
}

Here, the class TColor merely works as namespace for the methods that correspond to the Frege functions defined in the where block of the data definition.

All enumeration values are mapped to constants of type short, and hence values of different enumeration types cannot be distinguished any more on the Java level.

Type renamings, aka "newtype"

A type with exactly one constructor that has exactly one field is called a "newtype" (named after the keyword used to create this in Haskell, in Frege we use an oridnary data declaration). It is special, because it exists only at compile time. At runtime, all values of such a type appear with the type of the single component:

--- type to hold a currency name like @"EUR"@ or @"NZD"@
--- useful to avoid confusion with ordinary 'String's
--- Note: construction and destruction is a no op at runtime.  
data Currency = Currency String where
    foo :: Currency -> String
    foo (Currency s) = "We pay in " ++ s

main _ = println euro.paying
    where euro = Currency "EUR"

This compiles as follows:

public static abstract class TCurrency  {
   final public static java.lang.String paying(final java.lang.String arg$1) {
       return PreludeBase.TStringJ._plus_plus("We pay in ", arg$1);
   }
}
final public static frege.runtime.Lambda _main(final frege.runtime.Lazy arg$1) {
    return Prelude.println(PreludeText.IShow_String.it, TCurrency.paying("EUR"));
}

The TCurrency class is just a namespace to hold items declared in the where clause, in our case the function paying. Note how the argument is just a String. Note also that the construction of the euro value has vanished and just the Stringis passed.

Like with all functions, the method implementing the paying function is a static one. You'll need a qualified name like TCurrency.paying to access it from java code.

Clone this wiki locally