3.2.  class Sites

Java classes can be imported into Orc as sites using the class declaration. Imported classes must be in the classpath of the JVM running the Orc interpreter. The following sections describe in detail how such imported classes behave in Orc programs.

3.2.1. Dot Operator

x.member, where x evaluates to a Java class or object, is evaluated as follows:

  • If x has one or more methods named member, a "method handle" site is returned which may be called like any other Orc site. When a method handle is actually called with arguments, the appropriate Java method is selected and called depending on the number and type of arguments, as described in Method Resolution below.
  • Otherwise, if x has a field named member, the object's field is returned, encapsulated in a Ref object. The Ref object has read and write methods which are used to get and set the value of the field.

Note that no distinction is made between static and non-static members; it is an error to reference a non-static member through a class, but this does not change how members are resolved. Note also that if a field shares a name with one or more methods, there is no way to access the field directly.

The following (rather useless) example illustrates how the dot operator can be used to access both static and non-static methods and fields:

{- bind Integer to a Java class -}
class Integer = java.lang.Integer

{- call a static method -}
val i = Integer.decode("5")
{- read a field -}
val m = Integer.MIN_VALUE.read()
{- write a field -}
Integer.MIN_VALUE.write(5) >>
{- call a non-static method -}
i.toString()

3.2.2. Direct Calls

When x evaluates to a Java object (but not a Java class), the syntax x(...) is equivalent to x.apply(...).

When x evaluates to a Java class, the syntax x(...) calls the class's constructor. In case of overloaded constructors, the appropriate constructor is chosen based on the number and types of arguments as described in Method Resolution.

3.2.3. Pattern Matching

If C is bound to a Java class, it can be used as a pattern. A pattern C(x) matches any Java object of that class or any of its subclasses. The variable x is simply bound to the object again; thus the matcher is just a partial identity function.

3.2.4. Method Resolution

When a method handle is called, the actual Java method called is chosen based on the runtime types of the arguments, as follows:

  1. If only one method has the appropriate number of arguments, that method is called.
  2. Otherwise, each method taking the appropriate number of arguments is tested for type compatibility as follows, and the first matching method is called.
    1. Every argument is compared to the corresponding formal parameter type as follows. All arguments must match for the method to match.
      1. If the argument is null, then the argument matches
      2. If the formal parameter type is primitive (int, char, float, ...) and the argument is an instance of a wrapper class, then the argument is unboxed (unwrapped) and coerced to the type of the formal parameter according to Java's standard rules for implicit widening coercions.
      3. If the formal parameter type is a primitive numeric type and the argument is an instance of BigDecimal, the argument is implicitly narrowed to the formal parameter type.
      4. If the formal parameter type is a primitive integral type and the argument is an instance of BigInteger, the argument is implicitly narrowed to the formal parameter type.
      5. Otherwise, the argument must be a subtype of the formal parameter type.

The reason for the unusual implicit narrowing of BigDecimal and BigInteger is that Orc numeric literals have these types, and it would be awkward to have to perform an explicit conversion every time such a value is passed to a Java method expecting a primitive.

Currently we do not implement specificity rules for choosing the best matching method; the first matching method (according to some unspecified order) is chosen. Note also that we do not support varargs methods explicitly, but instead varargs may be passed as an array of the appropriate type.

3.2.5. Orc values in Java

Orc values are implemented by Java objects, so in general any Orc value may be passed to a site implemented in Java. Standard Orc values have the following Java types:

string
java.lang.String
boolean
java.lang.Boolean
number
java.lang.Number
tuple
orc.runtime.values.TupleValue
list
orc.runtime.values.ListValue
function
orc.runtime.values.Closure

Currently it is not possible to call Orc functions from Java code.

site
orc.runtime.values.Site

Currently it is not possible to directly call Orc sites from Java code. However if you are implementing a site yourself, you may provide methods which can be called from Java code to invoke the behavior of the site.

3.2.6. Java Values in Orc

Java objects may be used directly as values anywhere in an Orc program. Primitive Java values cannot be used directly in an Orc program, but are automatically boxed (and unboxed) as necessary.

When both arguments of an arithmetic or comparison operator are Java numeric types, the arguments are implicitly coerced to the widest of the two argument types. "Widest" is defined by the following relation, where ">" means "is wider than": BigDecimal > Double > Float > BigInteger > Long > Integer > Short > Byte

3.2.7. Cooperative Scheduling and Concurrency

3.2.7.1. Overview

In order to support massive concurrency efficiently in Java, Orc uses cooperative threading. Orc programs are broken into discrete steps which are executed by a fixed-size thread pool. This approach works for Orc expressions, but the internals of an Orc site written in Java cannot be easily broken down. So Orc has two choices when it needs to call a local Java site:

  • Run the site call in a new thread. This means site calls never unnecessarily block the Orc engine, but it introduces the potentially-unnecessary overhead of creating and context-switching threads.
  • Run the site call in the same thread as the Orc engine, which is efficient and conserves thread resources but may block the engine from making further progress until the site call finishes.

Fortunately, in practice most Orc site calls take a short, deterministic amount of time to complete. Of those that don't, it's usually only because they are blocked waiting for some external event, like another site call. In this case, instead of blocking the site can return control to the Orc engine, asking to be run again when the external event occurs. In typical Java applications this kind of non-blocking behavior is implemented using callbacks (or in its most general form, continuation passing). Unfortunately this creates convoluted and verbose code: what if you need to block in the middle of a for loop, for example?

Kilim resolves this issue by allowing programmers to write code in a natural blocking style and then rewriting the bytecode to perform a form of CPS conversion, so that instead of blocking the code actually returns control to a scheduler. Orc supports Kilim, allowing site authors to write sites with internal concurrency and blocking behavior which don't use Java threads and cooperate with the Orc engine.

3.2.7.2. Kilim Fundamentals

For a full introduction to Kilim, see A Thread of One's Own, by Sriram Srinivasan. In the following, we will cover just enough to get you started writing Orc sites using Kilim.

Kilim introduces three key primitives:

Pausable

This checked exception marks methods which may block. You should never catch this exception, and you should always declare it even if you have already declared throws Exception or throws Throwable. In all other respects it follows the normal rules for checked exceptions: an override can only throw it if the superclass method throws it, and you must throw it if you call any method which throws it.

Originally Kilim used annotations to mark pausable methods. It turned out that javac would sometimes manipulate code in ways which violate the invariants regarding use of the annotation. Annotations also have the disadvantage that the invariants on their use are not checked automatically by tools like Eclipse.

Mailbox

A multiple-producer, single-consumer queue used to communicate between tasks. The most important methods are put(Object), which places an item in the mailbox, and get(), which blocks until the mailbox is non-empty and then returns the first item.

Task
Kilim analogue of a Thread. Every Pausable method must be run within a task. To create and run a task:
new Task() {
    public void execute() throws Pausable {
        // do some stuff
    }
}.start();

Together, these primitives allow you to write highly-concurrent Java programs in a familiar threaded style. Under the hood, Kilim can schedule any number of concurrent tasks on a fixed number of physical threads, providing better scalability and performance than possible with Java threads.

When writing Orc sites you rarely need to use tasks explicitly because every Orc site call is implicitly run inside a Kilim task if necessary. Therefore all you need to do is mark Pausable methods. Here's a short example of a buffer site written using Kilim Mailboxes:

public class KilimBuffer<V> {
	private LinkedList<Mailbox<V>> waiters =
		new LinkedList<Mailbox<V>>();
	private LinkedList<V> buffer = new LinkedList<V>();
	public synchronized void put(V o) {
		Mailbox<V> waiter = waiters.poll();
		if (waiter != null) waiter.putnb(o);
		else buffer.add(o);
	}
	public synchronized V get() throws Pausable {
		V out = buffer.poll();
		if (out != null) return out;
		else {
			Mailbox<V> waiter = new Mailbox<V>();
			waiters.add(waiter);
			return waiter.get();
		}
	}
}

Classes using Kilim can be imported like other Java classes using the Orc class declaration. For example, we can use the KilimBuffer class defined above:

class Buffer = orc.lib.state.KilimBuffer
val b = Buffer()
  Rtimer(1000) >> b.put("1 second later") >> null
| b.get()

Warning

The version of Kilim bundled with Orc does not support calls to pausable methods in constructor arguments. For example, this does not work:
new Foo(bar.pausable());
The workaround is to use a temporary variable:
Object tmp = bar.pausable();
new Foo(tmp);

3.2.7.3. When you must block

Sometimes blocking is unavoidable, for example if a site must perform blocking IO. For such cases, Orc provides a utility method orc.runtime.Kilim#runThreaded(Callable) which farms work out to a thread pool. The advantage of doing this over spawning your own thread is that there is no chance of using too many threads; if no thread is available, the method pauses until one becomes available. The disadvantage is that if you have too many Java methods which must communicate with each other concurrently and can't use Mailboxes, there won't be enough threads for them all and execution will deadlock. This situation is sufficiently rare that runThreaded is usually the correct approach.

3.2.7.4. Compiling Kilim Sites

Code which uses Kilim must be processed with the Kilim "weaver" after compiling and before running. The weaver is bundled with the Orc source code, or may be downloaded separately from the Kilim project site.

To process compiled classes, run kilim.tools.Weaver. If your .class files are in the build/ directory, you would run the weaver like this: java -cp kilim.jar:build/ kilim.tools.Weaver -d build/ build/

The Orc source distribution includes an ant kilim task (in build.xml) which weaves compiled classes in the build directory. If you are compiling Orc in Eclipse, an Eclipse "Builder" is included which does this automatically whenever any source files change.