Foreign code is an upcoming feature in Fuse which lets you include and mix Objective-C and Java with JavaScript and UX markup.

A sane way of mixing languages in Fuse

by Chris of the Fuse team

Hey folks, today I want to talk about an upcoming feature in Fuse called foreign code — what it is, why we made it and where its evolving.

Note: this is the first step in our new approach to cross-language interop. It is currently heavy on the Uno, but JavaScript will be joining the party of course. Ultimately, how this will look when it ships is still open for discussion so please hurl opinions at us!
Update: we’ve just published updated documentation on Foreign Code, you should read up on our site!
Update 2: we’ve published the first in a series of video tutorials on foreign code — check it out!

Background

First, let’s recap why we need this stuff at all. We all need to make awesome apps without the need to reinvent the wheel every time. Even though we see ways in which app development could be way better, we also need to be able to leverage the crazy amount of functionality that already exists and that people know how to work with. We also hate when a platform stops us from doing something we know how to do (the classic sandbox issue). So, less of that please!

We also love native and are proud that we (and you) can easily reach down through the abstractions to get things done when it makes sense to do so. From JS to Uno, and from Uno to C++/ObjC/Java. Being able to define these boundaries in a non-hacky way makes the intent clear without hand-holding. More of that!

However, the majority of the awesome platform features don’t live in C++ (and even if they can be accessed there it might not be pretty or even practical), so we started looking at generating bindings to native Java and ObjC APIs.

Enter the bindings

Bindings are the first thing that pops to mind when we start looking at this problem. Make Uno entry-points for all the Java/ObjC methods and let people use them, it sounds so easy…

Turns out that getting 100% binding coverage of other languages is hard, even when they are a superficially similar as Java and Uno.

For those of you who like concrete examples lets take return values in Java:

Java return types can be co-variant, C#/Uno’s are not. Now as a thought exercise, imagine the extra methods you have to add to cover this case. Image what adding these extra methods to an abstract class does to it’s descendants, how those changes affect how people who expect the bindings to map 1 to 1 with Java APIs

Even ignoring that tiny example there are plenty more, and that’s just for Java, Objective-C gets even more ‘fun’ (it really, really doesn’t).

Regardless, we did this and got it working. Then came the test to see if it was a good workflow experience.

Powerful, but not quite what we thought

Here is the crux:

When you have a really easy scripting environment with many decent features, the kind of people who are trying to use bindings are generally people who know exactly what they want already, and most likely how to write it.

The fact above makes ANY difference between the bindings and the target language a source of annoyance. You get that uncanny valley feeling.

Behind the scenes, we have been doing experiments with other kinds of bindings as well, to see if we could figure out where the sweet spot was.

For example, we prototyped a JavaScript-to-Java binding library like you may have seen in other projects. Turns out that writing Java in JS can be pretty painful too as things like method overloading make it hard to know what you are calling, like:

Is a.Foo(1,2) in JS calling Foo(int,int) or Foo(long,long)?

To be sure about the answer to that question you have to know how the run-time type scoring works (ick!).

It sounds obvious, but language APIs are shaped by the language features (like static typing) and using them from languages with different features can feel awkward.

This is all to say the following:

We have written many kinds of bindings systems… and we have become exceedingly efficient at it.

So where does that leave us?

Well — we have this sweet platform. JavaScript makes scripting familiar and super easy, and Uno makes performant cross-platform code a breeze to write. We need something lightweight that lets you write the little bit of Java/ObjC that you already know how to write and not get in the way.

Foreign code is the start of a potential answer to this.

The meat!

The foreign code feature allows you to make an Uno method where the body of the method is in a different programming language. For example:

public class SomeUnoClass
{
[Foreign("Java")]
public extern(android) void Ping()
{
//this is Java code
Runnable r = new Runnable() {
public void run() {
Log.d("SomeApp","WAT!");
}
}
r.run();
}
}

We support automatic conversion of primitive and string types.

[Foreign("Java")]
public extern(android) string GetTheCityName(double lat, double long)
{
// lat and long are coverted from Uno double to Java double
// and the return it coverted to an Uno string
return Some.convincing.map.api.call(lat, long);
}

If you pass an Uno object to Java it gets boxed as the opaque UnoObject type. If you pass an Java object to Uno it gets boxed as the opaque Java.Object type.

[Foreign("Java")]
public extern(android) Java.Object GetDooHickey()
{
return new DooHickey();
}

This avoids all the mapping between object models that we saw gave so much grief. When we need to manipulate the foreign object we do it in a simple foreign method.

When it is this easy to get to the language you need to write in, the rule becomes: If you need to do something to an Java object, do it in Java and we make it trivial to get your data there.

Of course, the same applies to Objective-C.

You can also use UXL macros inside foreign methods. This means you can access Uno fields and call Uno methods from Java and Objective-C

public class SomeUnoClass
{
int someUnoField = 10;
    [Foreign("Java")]
public extern(android) double Yup(double x)
{
return x + @{someUnoField:Get()};
}
}

What about when you need to write a lot of code?

If you need to write a large chunk of Java or Objective-C code.. then do it.. in a Java or Objective-C file and let Fuse take care of adding it to your project!

We have updated the unoproj format so that you can include C/ObjC & Java files in your files list. The compiler will make sure they end up in the correct place (so for you Java-folks: no more having to have a directory structure that matches your package name).

And to call out to these foreign code files? Just use foreign methods!

As cool as it may be to try and make an amazing IDE for everything, we respect the huge amount of work that goes into the various tools and would rather people can use the best Java and Objective-C editors that exist today.

Here at Fuse we are driven to not only make the hard stuff easier, but also with letting people work well with the tools they already know.

What we get

So what did we get out of this?

  • Speed: The types are known at compile time and the conversions are written by the compiler.
  • Simplicity: There are very few working parts to remember, so you never have to try a reason about what is getting called.
  • Version Agnostic: Bindings can break when a new API update is released and you have to wait until the team can regenerate, test and release. Foreign Code doesn’t have this problem.
  • Fewer corner cases: You can take the code from StackOverflow and stick it in a foreign method without worrying that there may be slight api differences between the target language and your bindings.
  • Less hand-holding: You are likely only using this because of a particular feature that you need, so we get out of your way and let you get it done.

Where we can go

Clearly this is a feature that can grow, but we are going to do so with care to ensure we don’t recreate the pain we are escaping. One feature on the roadmap is to allow the passing of Uno delegates to the foreign methods and have them converted to Callables/Lambdas.

As mentioned at the very start this first version of the foreign code feature still has Uno as the central hub between all the other languages. There is no reason this has to stay this way. For example, we’re considering adding tags to Java code to allow it to be called from JS, maybe having JS methods with Java bodies would work (just like we have now in Uno).

It’s going to be fun to try hammer these ideas out and see what works and feels best.

Right folks, hope you like the sneak-peak. As always, please tell us your thoughts, these things are not solved problems and it’s exciting to make headway here, and the best way to do that is not to do it in a vacuum.

PS: if you haven’t already, download Fuse and give it a go! There’s documentation, our forum and a thriving Slack community as well.

Ciao,
Chris