Skip to content

Instantly share code, notes, and snippets.

@sorear
Created July 8, 2013 08:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sorear/5947073 to your computer and use it in GitHub Desktop.
Save sorear/5947073 to your computer and use it in GitHub Desktop.
Rakudo Java interop UI goals for jnthn

Introduction

This infodump describes aspects of the most desirable interface for Java interop functionality in Rakudo, based on experience with CLR interop functionality in Niecza. No aspects of implementation per se are discussed.

Unsolved questions

Mainly, "should wrapper types be invariant or covariant?". A wrapper of type Subclass is not necessarily substitutable for a wrapper of type Superclass, because the subclass may have added methods which cause an ambiguous overload (in the CLR we have the additional issue of shadowing, but that doesn't apply here). Invariant wrappers (all wrappers are assignable to JavaObject and Any) are the safer option, but covariant wrappers (reflecting the Java type DAG) would be more convenient in some cases. I did covariant wrappers for Niecza and was not quite happy with the associated traps; I'd like to see if invariant wrappers can be made to work.

General object features

We'd probably like fields to be reflected as rw methods, returning Proxy objects that handle get and set. (This works well in Niecza).

Methods, including new aka <init>, should be bundled by short name and then turned into overload-resolving sets. This can be done either by creating a composite method that implements Java overload rules, or by building a Perl 6 multi-dispatcher that wraps a set of methods. Using a multi-dispatcher would probably require covariant wrappers.

Java objects should be made to implement core Perl 6 roles whenever reasonable. MethodHandles and single-method interfaces (including the new single-method interfaces being added to Java 1.8 for use with lambdas) should function as Callable with a postcircumfix:<( )>. Arrays and objects assignable to java.util.List should present a Positional interface. Objects assignable to java.util.Map should present an Associative interface. java.util.Iterable things should probably be iterable, somehow.

It may additionally be useful to provide semantics for java.util.Collection and java.util.Set, but that's probably overkill.

It shall be possible to throw and catch Java exceptions as Perl 6 exceptions. (The current implementation is limited such that you can throw OR catch Java exceptions - a thrown Java exception is treated as a direct control operator to the innermost callback, and cannot be caught by Perl 6 exception handlers until it propagates to a callout point.)

Since a java.lang.ClassLoader is effectively the root of a class namespace, it shall be possible to obtain a PsuedoStash-like object for a class loader which can be used to fetch type objects representing the classes defined in it. Each such PsuedoStash holds a class loader and a package prefix; fetches attempt a lookup and return a concatenated stash, along with either a type or some kind of Uninstantiatable thing.

Marshal-in (boxing) rules

Most objects should box as JavaObjects. In order for the interface to be most generally usable, it must be possible to get the exact same Java object out of the box - boxings that lose object identity are probably unacceptable. Primitive types have no such restriction and should be translated into Perl 6 types freely. BootJavaObject already translates all primitives that correspond to 6model types in the obvious way, additionally char is returned as a one-character str and boolean is mapped to an int of 0 or 1. The former is probably correct for Perl 6, but the latter should be overridden to be True or False.

It is acceptable to map reference types in cases where the Perl 6 object wraps the corresponding type, so it can be recovered identically from the box. This is the case for java.lang.String / Str and java.math.BigInteger / Int, and of course SixModelObject.

Marshal-out (unboxing / coercion) rules

In all cases it is acceptable to pass a JavaObject wrapper to code which expects a reference type. The type of the wrapper is ignored, except possibly for overload resolution. Primitive types and naturally-boxed types, including SixModelObject, are marshaled out in the obvious way.

It may be useful to provide special handling for functions which take an argument of type ThreadContext or GlobalContext; note that BootJavaInterop specially allows passing nqp::null() in such cases with an interpretation of "current". Alternatively mechanisms can be provided in the library for retrieving the current context objects.

Wherever an Iterator or Iterable is needed, it shall be acceptable to pass an iterable Perl 6 object; a wrapper Iterable shall be constructed which iterates the underlying Perl 6 object.

Wherever a List is needed, it shall be acceptable to pass any Perl 6 object that does Positional. Note that implementing the List interface faithfully requires splice and elems in addition to at_pos, while exists and delete are not used.

Wherever a Map is needed, it shall be acceptable to pass any Perl 6 object that does Associative.

Wherever any non-collection interface type is needed, it shall be acceptable to pass a hash mapping string method names to code references, which results in the creation of a proxy class (as per BootJavaInterop.proxy). If the interface has a single method, passing a single code reference is acceptable. Collection interfaces must be proxied explicitly, to avoid ambiguity with the (presumably more common) case where the programmer wishes to treat a hash as a collection itself.

Access

It is inevitable that some general utilities not tied to a specific class will be needed. A setting module (JavaUtils::foo) is probably the sanest way to approach this.

Access to the class hierarchy is bootstrapped through a special symbol in the setting which is bound to a psuedo stash representing the combination of the NQP class loader (the class loader through which nqp-runtime.jar, rakudo-runtime.jar, and (through delegation) rt.jar are visible) and the empty package prefix. Since the NQP class loader itself cannot and should not be serialized, it will probably be necessary to represent this in a special way, for instance by allowing a null class loader to represent the NQP class loader. (Note that this is inconsistent with the usual behavior of Java core methods to treat a null class loader as a reference to the bootstrap class loader. That might be a problem.)

use can be wired up to bind class and package symbols into the current scope. Additionally we can support adverbs which trigger dynamic loading of jars.

POD ERRORS

Hey! The above document had some coding errors, which are explained below:

Around line 33:

Non-ASCII character seen before =encoding in 'type C<ThreadContext>'. Assuming UTF-8

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment