Java

Calling Java functions

Consider a typical Java "Hello, world" program:

import java.lang.System;

class Main {
	public static void main(String[] args) {
		System.out.println("Hello, world!");
	}
}

To translate it directly to Keen:

import java/io/PrintStream java/lang/System main void() java, global trusted println out, "Hello, world!"

An import beginning with java/ imports a class from the Java standard library.
(Replace . with /. So java/io/PrintStream imports java.io.PrintStream .)

Keen is not Java and doesn't have many of Java's features, such as classes and methods.
When Keen imports a Java class, the class' public properties and methods are translated to functions.

  • A static field becomes a function with no arguments. The class name is omitted.
    For example, Java's System.out becomes out in Keen.
  • A static method can be called directly. The class name is omitted.
    For example, Java's Math.sqrt becomes sqrt in Keen.
  • An instance field becomes a function taking the class as an argument.
    For example, Java's Point.x instance field becomes a function x in Keen.
  • For an instance method, a this argument of the class' type is prepended to the arguments.
    For example, the instance method println takes a java/io/PrintStream (such as out) as its first argument.
  • A class constructors becomes a function named new.

All functions that come from Java have the java, global, and unsafe specs.

In Java, methods on a class can be called anywhere without importing the class. Keen doesn't work like that; if you want to call a method, you need to import the class.
The Java program above only needed import java.lang.System;, while the Keen version needs to import both java/io/PrintStream and java/lang/System.

Null values

In Java, all reference types have a null value.
Keen considers null values to be unsafe, so don't don't mark a call to a function that comes from Java as trusted unless you are sure it won't return null.

java-null is an unsafe function returning a Java null value for a type.
As a convenience, is-null a is equivalent to reference-equal a, java-null.

The below example reads a directory, handling the case where it does not exist.

import keen/java java/lang/String java/io/File main void() java, global log get-files "some-dir" get-files string[](dir-name string) java, global trusted dir File = dir-name.to::String, files String buffer = list dir forbid is-null files else error "Directory does not exist" for file in files file.to::string

Casting

Java subclasses implicitly convert to their superclasses (and interfaces).

For reference types, reinterpret-cast corresponds to a Java cast.
This can be used to convert a Java class to a subclass.

import keen/unsafe java/lang/Object java/lang/String main void() java, global, unsafe a String = "hello" b Object = a c String = reinterpret-cast b log c.to

Generics

Keen supports Java generic types such as List.

import java/lang/Iterable java/lang/String java/util/Iterator java/util/Arrays java/util/List keen/java main void() java, global, unsafe buf String buffer = "hello", "world" list String List = asList buf iterable String Iterable = list iterator String Iterator = iterator iterable log to::string next iterator log to::string next iterator log hasNext iterator

Type translation

Java types translate to the following Keen types:

JavaKeen
voidvoid
boolbool
byteint8
shortint16
intint32
longint
floatfloat32
doublefloat64
t[]t buffer

Since Java Arrays are mutable, they are translated as buffer instead of array.
If a Java method treats an Array argument as read-only. you could use cast-mutable to pass in a Keen array.
If a Java method returns an Array that will never be mutated, you could use cast-immutable to get it as a Keen array.

import java/io/PrintStream java/lang/String java/lang/System main void() java, global, unsafe bytes int8[] = 104, 101, 108, 108, 111 # cast-mutable because 'new String()' does not mutate its argument s String = bytes.cast-mutable, println out, s

There are additional functions in keen/unsafe for converting between types that have the same runtime representation, such as char8 buffer and nat8[]. (These work in any target, not just Java.)

Implementing Java interfaces

Keen record types can implement Java interfaces directly. It works just like implementing a Keen interface.

import java/lang/Object java/util/Arrays java/util/Comparator main void() global, java, unsafe a point buffer = - 1, 2 - 2, 0 - 1, 1 # sort is from 'java/lang/Arrays' sort a, point-comparator log a point record x nat y nat <=> comparison(a point, b point) to json(a point) point-comparator record() point Comparator case equals bool(_ point-comparator, _ Object) # Comparator requires this, but it won't actually be used throw unreachable compare int32(_ point-comparator, a point, b point) match a <=> b as less then -1 as equal then 0 as greater then 1

Keen doesn't make use of Java generics, so:

  • Java's sort function acts on an Object buffer .
  • The interface is just Comparator with no type parameter.
  • The comparator accepts Object parameters and downcasts them using reinterpret-cast.

Nested classes

A nested class like Map.Entry is imported as Map$Entry. The name of the type is just Entry.

import java/util/Iterator java/util/Map java/util/Map$Entry java/util/Set main void() global, java, unsafe map (nat, string) Map = ofEntries - entry 1, "one" - entry 2, "two" iterator (nat, string) Entry Iterator = iterator entrySet map entry = next iterator log entry.getKey, entry.getValue

Keen types

There are no guarantees about how Keen translates to Java.
A Keen interface isn't always a Java interface.
A Keen record isn't always a Java record.
So don't expect to use Java reflection.