In the last post, we have prepared an Eclipse plugin project for a Hello-World Cohatoe plugin. Lets have a look at the Haskell side now.
The Haskell code that we are going to contribute will be loaded at runtime by the Cohatoe server core plugin, which in turn delegates the work to hs-plugins. hs-plugins is a library by Don Stewart written for exactly this purpose: dynamically locating compiled Haskell code at runtime, loading it and executing it, and all this in a typesafe manner. (hs-plugins can also load and on-the-fly-compile Haskell source code, but that's not a functionality I'm using.)
Haskell code that can be loaded this way has to fulfill a few conditions: it must provide, by convention, a symbol called 'resource', which is a record and has a field, pluginMain :: [String] -> [String], which is the entry point function that Cohatoe will call. (Think of it as a fancy sort of main function.) Therefore, all Haskell code that is contributed via Cohatoe will include something like this after the module declaration:
-- We must import Cohatoe.API and implement resource
-- so that this code can be dynamically loaded
import Cohatoe.API
resource = plugin {
pluginMain = sayHello
}
-- our code starts here
-- ...
sayHello = ...
As you can see, this needs to import the Cohatoe API. Let's make sure that we have that API available.
Remember that you downloaded a second file,
cohatoe-api_0.1.0.zip? Unzip that archive now and open a command prompt in the cohatoe-api/ folder:
> cd C:\cohatoetest\cohatoe-api
Now run the following three commands:
> runhaskell Setup.hs configure
> runhaskell Setup.hs build
> runhaskell Setup.hs install
This compiles the
cohatoe-api package (which is very small and simple), installs it and registers it as a package for GHC.
Now that we have this in place, we can compile the Haskell code that we want to call from our Eclipse plugin. Create a file, e.g.
Example.hs, with this content:
module Example where
import Cohatoe.API
resource = plugin {
pluginMain = sayHello
}
sayHello :: [String] -> [String]
sayHello _ = ["Hello from Haskell"]
and compile it like so:
> ghc -c -package cohatoe-api Example.hs
Put the resulting
Example.o and
Example.hi files into a folder called
os/win32/x86/obj/ in your plugin:

(You might have guessed that there is a naming convention for OS platforms at work here in this particular name for the folder. You're right. I'll elaborate on this in a later post.)
The next thing to do now that we have our Haskell object code, is to register it with Cohatoe as an executable function. This is done via Eclipse's extension mechanism. Cohatoe provides an
extension point, and we'll provide an Eclipse
extension to that extension point now.
Open the Plugin Manifest Editor by double-clicking the file
plugin.xml in the plugin project. First switch to the
Dependencies tab. In the
Required section, press
Add and select
de.leiffrenzel.cohatoe.server.core. By doing this, we make our new plugin dependent on the Cohatoe server core plugin, which we must do because we want to use its extension point. Now switch to the
Extensions tab and press
Add. Choose the
haskellFunctions extension point (actually, it appears under its fully qualified name, which is
de.leiffrenzel.cohatoe.server.core.haskellFunctions), then right-click it and select
New > haskellFunction. Here you get a form where you can fill in the details for the Haskell Function extension that we are just creating:

As you can see, we have basically to specify three things: a Java interface, a Java class that implements that interface, and the location of the object code of our Haskell function. For the latter, just specify the
.o-file, i.e.
$os$/obj/Example.o. We only need to specify the location of the
.o-file, the location of the corresponding
.hi-file is automatically determined by Cohatoe from that location. (But the
.hi-file must be there in the same folder as the
.o-file, otherwise hs-plugins will not be able to load the code.)
And what about the interface and implementation class? These are registered together with the object code so that Java code from Eclipse plugins can call our Haskell function. That Java code will always only see the interface that you specify here. (We will see in a moment what exactly will be visible for any Java code that wants to call our Haskell function.) As a provider of a Haskell function, however, you also have to provide an implementation class for that interface. This is necessary for Cohatoe itself. It will instantiate that implementation class and pass it on to the clients that want to call the Haskell function (they will only see the specified interface as the type of the object that they get). The primary role of this implementation class is that it gives the provider of a Haskell function a hook to implement data marshalling. In our case, there is no data to be transported from the Java to the Haskell side, and only a minimum from the Haskell side to the Java side - thus the implementation class will not have to do much.
In the screenshot above you see that I have specified
cohatoetest.IExampleFunction as the name of the interface and
cohatoetest.ExampleFunction as the name of the implementation class. These two types do not yet exist. You can easily create them by clicking the link before the type name in the Manifest editor.
After you have created the source files, their content should look like this:
// cohatoetest.IExampleFunction:
package cohatoetest;
public interface IExampleFunction {
String sayHello();
}
// cohatoetest.ExampleFunction:
package cohatoetest;
import de.leiffrenzel.cohatoe.server.core.CohatoeServer;
public class ExampleFunction implements IExampleFunction {
public String sayHello() {
CohatoeServer server = CohatoeServer.getInstance();
String[] retVal = server.evaluate( IExampleFunction.class, new String[ 0 ] );
String result = "[Got no answer from Haskell.]";
if( retVal != null && retVal.length == 1 ) {
result = retVal[ 0 ];
}
return result;
}
}
The only interesting thing here is the implementation of
sayHello() in our implementation class. You see that there is a singleton object of type
CohatoeServer, to which the call to our function is delegated. The Cohatoe server singleton knows about all registered Haskell functions, and it knows how to forward calls to them. Since it is a singleton and shared between all plugins in the running Eclipse instance, we must pass a key to it so that it knows which Haskell function we actually want to evaluate. The Cohatoe server uses the class objects of the interfaces under which functions are registered as keys; you can see that we pass
IExampleFunction.class as a key here. We also pass an empty string array as parameter list. The return value (if everything goes fine) is a one-element
String array that holds the single string returned by our Haskell function.
(Note that this is a really, really trivial example. What you usually want to do in this implementation is to accept objects of all types as parameters and convert them into a string representation for marshalling, then convert back from strings what you receive as result from the call to the Haskell function. This is deliberately designed so that the interface for the Java code that calls the Haskell function is typesafe. So for example, instead of accepting a
String for a file name, you would use a
java.io.File object in the signature of your interface function, and only convert it into a string inside the implementation. Similarly, you should not return the bare strings that you get from the Haskell side, but instead convert them into objects of a type that really represent your data.)
This is all - we have now made our Haskell function available to any Java code in any Eclipse plugin that wants to call it. (Strictly speaking, we have made it available to any Eclipse plugin that depends on our own plugin.) To demonstrate how one would call it, we'll put such a call into the
run() method of our 'Hello-World' action. Remember, this was in the class
cohatoetest.actions.SampleAction. Modify the
run() method so that it looks like this:
public void run( final IAction action ) {
CohatoeServer server = CohatoeServer.getInstance();
Object fun = server.createFunction( IExampleFunction.class );
if( fun instanceof IExampleFunction ) {
IExampleFunction helloFunction = ( IExampleFunction )fun;
MessageDialog.openInformation(
window.getShell(),
"Cohatoetest Plug-in",
helloFunction.sayHello() );
}
}
You see again that we use the type of our declared interface as key, this time to obtain an object that (sort of) represents our Haskell function, so that we can call it via that object. The rule is that you can get an object complying to the interface that you pass in as key, or
null (if no function is registered for that interface). Then you can cast and pass your function arguments in a typesafe manner.
Voila, if you run this, you'll see a friendly message from the Haskell side :-)

In my next post, I'll explain some troubleshooting aids that come with Cohatoe - just in case you encountered any problems on the way.