23

I googled and got some answers that communication between Java and Haskell can be done by GCJNI(Now the site is down) and LambdaVM.. To use the LambdaVM/GCJNI, whether I need to download any build tools? Where can I know more about them, since I don't find much resources on online?

I want to develop an application that communicates between Java and Haskell(Where I will get the input from Java pass it to the Haskell and process there and return the result back to Java).This is what I want to do. Please help me...

  • 1
    I don't know if this could be an option in your case, but there is a very "haskellish" language called Frege on the JVM: http://code.google.com/p/frege/ – Landei Apr 29 '12 at 14:21
  • Since you have not accepted any of the answers below, what more do you need to know? – Samuel Audet Jun 13 '12 at 07:41

4 Answers4

41

Calling Haskell from C appears quite easy, and thus can also be easily called from Java with JavaCPP. For example, to call the fibonacci_hs() function from the sample code Safe.hs:

{-# LANGUAGE ForeignFunctionInterface #-}

module Safe where

import Foreign.C.Types

fibonacci :: Int -> Int
fibonacci n = fibs !! n
    where fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

fibonacci_hs :: CInt -> CInt
fibonacci_hs = fromIntegral . fibonacci . fromIntegral

foreign export ccall fibonacci_hs :: CInt -> CInt

we can do it this way from Java:

import org.bytedeco.javacpp.*;
import org.bytedeco.javacpp.annotation.*;

@Platform(include={"<HsFFI.h>","Safe_stub.h"})
public class Safe {
    static { Loader.load(); }
    public static native void hs_init(int[] argc, @Cast("char***") @ByPtrPtr PointerPointer argv);
    public static native int fibonacci_hs(int i);
    public static void main(String[] args) {
        hs_init(null, null);
        int i = fibonacci_hs(42);
        System.out.println("Fibonacci: " + i);
    }
}

Under Linux, the compilation procedure looks like this:

$ ghc -fPIC -dynamic -c -O Safe.hs
$ javac -cp javacpp.jar Safe.java
$ java -jar javacpp.jar -Dplatform.compiler=ghc -Dplatform.compiler.output="-optc-O3 -Wall Safe.o -dynamic -fPIC -shared -lstdc++ -lHSrts-ghc7.6.3 -o " -Dplatform.linkpath.prefix2="-optl -Wl,-rpath," Safe

And the program runs normally with the usual java command:

$ java -cp javacpp.jar:. Safe
Fibonacci: 267914296


Edit: I have taken the liberty to do some microbenchmarking of the calling overhead. With the following C header file Safe.h:
inline int fibonacci_c(int n) {
    return n < 2 ? n : fibonacci_c(n - 1) + fibonacci_c(n - 2);
}

the following Java class:

import org.bytedeco.javacpp.*;
import org.bytedeco.javacpp.annotation.*;

@Platform(include={"<HsFFI.h>","Safe_stub.h", "Safe.h"})
public class Safe {
    static { Loader.load(); }
    public static native void hs_init(int[] argc, @Cast("char***") @ByPtrPtr PointerPointer argv);
    public static native int fibonacci_hs(int i);
    public static native int fibonacci_c(int n);
    public static int fibonacci(int n) {
        return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
    }
    public static void main(String[] args) {
        hs_init(null, null);

        for (int i = 0; i < 1000000; i++) {
            fibonacci_hs(0);
            fibonacci_c(0);
            fibonacci(0);
        }
        long t1 = System.nanoTime();
        for (int i = 0; i < 1000000; i++) {
            fibonacci_hs(0);
        }
        long t2 = System.nanoTime();
        for (int i = 0; i < 1000000; i++) {
            fibonacci_c(0);
        }
        long t3 = System.nanoTime();
        for (int i = 0; i < 1000000; i++) {
            fibonacci(0);
        }
        long t4 = System.nanoTime();
        System.out.println("fibonacci_hs(0): " + (t2 - t1)/1000000 + " ns");
        System.out.println("fibonacci_c(0): "  + (t3 - t2)/1000000 + " ns");
        System.out.println("fibonacci(0): "    + (t4 - t3)/1000000 + " ns");
    }
}

outputs this with an Intel Core i7-3632QM CPU @ 2.20GHz, Fedora 20 x86_64, GCC 4.8.3, GHC 7.6.3, and OpenJDK 8:

fibonacci_hs(0): 200 ns
fibonacci_c(0): 9 ns
fibonacci(0): 2 ns

In all fairness, I should mention that it is also pretty expensive to call into the JVM as well...


Update: With recent changes to JavaCPP, users can now access callback function (pointers) by name from C/C++, making it possible to call into the JVM from Haskell easily. For example, based on information found on a wiki page regarding Haskell's FFI, with the following code placed in Main.hs:
{-# LANGUAGE ForeignFunctionInterface #-}
module Main where

import Foreign.C -- get the C types
import Foreign.Ptr (Ptr,nullPtr)

-- impure function
foreign import ccall "JavaCPP_init" c_javacpp_init :: CInt -> Ptr (Ptr CString) -> IO ()
javacpp_init :: IO ()
javacpp_init = c_javacpp_init 0 nullPtr

-- pure function
foreign import ccall "fibonacci" c_fibonacci :: CInt -> CInt
fibonacci :: Int -> Int
fibonacci i = fromIntegral (c_fibonacci (fromIntegral i))

main = do
  javacpp_init
  print $ fibonacci 42

and a fibonacci function defined in Java this way:

import org.bytedeco.javacpp.*;
import org.bytedeco.javacpp.annotation.*;

@Platform
public class Main {
    public static class Fibonacci extends FunctionPointer {
        public @Name("fibonacci") int call(int n) {
            return n < 2 ? n : call(n - 1) + call(n - 2);
        }
    }
}

we may build under Linux x86_64 with something like:

$ javac -cp javacpp.jar Main.java
$ java -jar javacpp.jar Main -header
$ ghc --make Main.hs linux-x86_64/libjniMain.so

and the program executes correctly giving this output:

$ ./Main
267914296
Samuel Audet
  • 4,964
  • 1
  • 26
  • 33
  • 3
    Now if only someone produced an example in the opposite direction (haskell -> java) – Vagif Verdi Apr 30 '12 at 17:30
  • @Vagif HJVM appears to be the tool you are looking for: http://hackage.haskell.org/package/HJVM-0.1 – Samuel Audet May 03 '12 at 02:55
  • @Vagif BTW, JavaCPP supports C callback functions (`FunctionPointer`) and Haskell FFI also supports C function pointers (`FunPtr`), so Haskell can call into Java that way too. If you are looking for something like that, let me know. – Samuel Audet Jun 05 '12 at 03:12
  • @SamuelAudet Hey, I'm trying to follow the steps, but for some reason a Safe_stub.o file is never generated, any idea why? – Charles Durham Jun 07 '12 at 19:29
  • @Ratzes I am not sure why no... I am guessing it must have something to do with the Haskell FFI not working properly on your installation for some reason. – Samuel Audet Jun 08 '12 at 05:11
  • @SamuelAudet, hey, just posted this http://stackoverflow.com/questions/10983728/ghc-7-4-1-not-producing-stub-o-files apparently stub.o and stub.c files are no longer generated post ghc 7.2 – Charles Durham Jun 11 '12 at 17:10
  • @CharlesDurham Ah, that's good to know. Please feel free to edit my answer to add a `java -jar javacpp.jar` compile command that works with ghc 7.2, thanks! – Samuel Audet Jun 12 '12 at 02:21
  • @SamuelAudet When I run the example above I get this error: # # A fatal error has been detected by the Java Runtime Environment: # # SIGSEGV (0xb) at pc=0xb765b616, pid=3859, tid=3063429952 # # JRE version: 7.0_05-b05 # Java VM: Java HotSpot(TM) Server VM (23.1-b03 mixed mode linux-x86 ) # Problematic frame: # C [libc.so.6+0x82616] envz_strip+0x1b6 – Radu Stoenescu Jun 20 '12 at 13:38
  • @SamuelAudet Calling haskell from C worked for me. I tried the above example-Calling Haskell from Java. I installed Java, Haskell 7.4.1, GNU C/C++ Compiler and regarding JavaCPP, I downloaded and unzipped. And I don't know how to proceed from there. I tried my level best in both Linux and Windows. Couldn't able to proceed. Can you help me with this in running the above example. Thanks – Thenraja Vettivelraj Jul 09 '12 at 19:37
  • @Thenraja In Linux, just copy/paste the commands in the terminal, with some adjustments for Haskell 7.x which I am not familiar with, but as noted by Charles. In Windows, you'll have to figure out if it wants GCC or MSVC, and then use that. Java and JavaCPP works with both anyway. – Samuel Audet Jul 10 '12 at 01:52
  • @SamuelAudet Thanks for the reply. When tried executing the program from the URL [JAVACPP](http://code.google.com/p/javacpp/) The applications/softwares that I have installed in Ubuntu inorder to run the program jehovah@ubuntu:~$ gcc --version gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3 jehovah@ubuntu:~$ make -v GNU Make 3.81 javac -version javac 1.7.0_03 java -version java version "1.7.0_03" OpenJDK Runtime Environment (IcedTea7 2.1.1pre) (7~u3-2.1.1~pre1-1ubuntu3) OpenJDK 64-Bit Server VM (build 22.0-b10, mixed mode) Netbeans version - 7.0.1 Then moved the javacpp.jar to the /lib – Thenraja Vettivelraj Jul 10 '12 at 21:58
  • @SamuelAudet When I tried executing the program [JavaCPP](http://code.google.com/p/javacpp/) jehovah@ubuntu:~/Desktop/P1$ javac -cp javacpp.jar LegacyLibrary.java LegacyLibrary.java:1: error: package com.googlecode.javacpp does not exist. In total I got 8 errors. I´m trying hard to figure out the errors. In the above comment I have listed that what I installed in my Ubuntu OS for your reference. Can you help me, please? Thanks Thenraja – Thenraja Vettivelraj Jul 10 '12 at 22:06
  • @Thenraja Is the `javacpp.jar` file in `~/Desktop/P1`? You should probably read up on Java if you intend to use it... [The Java Tutorials](http://docs.oracle.com/javase/tutorial/) are a good start. – Samuel Audet Jul 12 '12 at 03:42
  • @SamuelAudet Thanks for the guidance. When I tried to run the legacylibrary example by Netbeans after setting the JAVACPP.jar and others. The program available in the [JAVACPP](http://code.google.com/p/javacpp/).. I got the error as Exception in thread "main" java.lang.UnsatisfiedLinkError: no jniLegacyLibrary in java.library.path. Any idea to resolve the error? Is that the problem with LD LIBRARY PATH? Please guide me – Thenraja Vettivelraj Jul 16 '12 at 00:14
  • @Thenraja You need to call `java -jar javacpp.jar` as shown in the README.txt file. If you want more help regarding JavaCPP, please ask somewhere else. This has nothing to do with the question you asked about Haskell... – Samuel Audet Jul 16 '12 at 02:20
  • @Vagif Done! I recently updated JavaCPP with a way to call into the JVM and updated the answer accordingly, enjoy! Thenraja, is there anything missing from this answer?? Please accept it if it answers your question, or please let me know what is missing, thank you – Samuel Audet Sep 30 '12 at 14:10
  • Did you mean to call fibonacci(0) in all the calls in the benchmark? Surely you'd want to use an argument greater than 0? – Lii Dec 06 '12 at 11:56
  • @Lii Yes, I wanted to benchmark the call overhead only, not the `fibonacci()` function. – Samuel Audet Dec 07 '12 at 01:20
7

If you opt for the Haskell server process approach, you could use the MessagePack serialization/rpc library. It has bindings for both Java and Haskell , and the Haskell bindings seem to be well maintained. Look for msgpack and msgpack-rpc on Hackage.

Here's a toy example of Java/Haskell interaction using MessagePack: Java server, Haskell client (links go to GitHub). The communication is in the opposite direction of what you want, though.

danidiaz
  • 26,936
  • 4
  • 45
  • 95
4

It depends on how you want them to communicate. To have Java and Haskell code running natively in the same process and exchanging data in memory via their respective FFIs is a huge problem, not least because you have two GCs fighting over the data, and two compilers both of which have their own ideas about representing various data types. Getting Haskell compiled under the JVM is likewise difficult because the JVM does not (at present) have any concept of closures.

Of course these things can be done, but getting from demonstrator to industrial tool takes huge effort. My understanding is that the tools you mention never made it past the demonstrator stage.

A simpler, if less elegant, solution, is to write your Haskell program as a server process that is sent data over sockets from the Java. If performance and volume is not too high then coding it up in JSON would probably be simple, as both sides have libraries to support it.

Paul Johnson
  • 17,438
  • 3
  • 42
  • 59
  • it is not so much the lack of JVM closures. Many JVM languages have closures, technically so does Java with inner classes, and Java 8 has an elegant implementation of lambdas proposed. The problem is 1. no tail calls 2. allocator/GC that is non-optimal for functional languages (Haskell code allocates many more small short lived objects than Java). Still Frege etc show that it is possible. – Philip JF Apr 30 '12 at 22:06
  • You can get tail call elimination from a code manipulator like Kilim, but it isn't very convenenient in general https://github.com/kilim/kilim/blob/master/docs/IFAQ.txt – misterbee May 10 '12 at 14:23
1

TL;DR: Use message passing (ie RPC client-server or peers) pattern.

Why? It's safer, scalable, flexible and debuggable. Calling into FFI is going to be fragile and hard to test.


RPC frameworks/specifications

  • gRPC Google's public fork of Protobufs RPC over HTTP/2

  • msgpack-rpc Doesn't include a transport.

  • zerorpc ZeroMQ + msgpack. Only has Python and Node implementations. Seems abandoned too.

  • XML-RPC Mature. Wide interoperability but it's also XML.

  • JSON-RPC Easier to debug. Not a binary protocol, although BSON maybe hack into some libraries.


Serialization

  • Protocol Buffers (protobufs) There are many, many more tools for it than others. It supports versioned/optional data members that don't require recompiling (or breaking) the world to interoperate.

  • msgpack Cute, simple and efficient, but it doesn't support forward-compatible schema changes. It's purely a dumb, binary codec. Probably too simple and low-level for practical use.


Transports

  • ZeroMQ Probably the fastest, non-Infiniband/FC/10 GbE, non-thread-safe message transport.

  • Nanomsg A newer, thread-safe, UNIX-philosophy reimagining of ZeroMQ from one of its founders.

  • HTTP/2 (used by gRPC) The advantage here is it's a standard that works within and between datacenters, and also has TLS (gRPC native code uses BoringSSL, Google's "more secure" OpenSSL fork).

  • MQTT When you need to implement push notifications to a billion devices and use a human-readable protocol.