Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Communication between Java and Haskell

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...

like image 314
Thenraja Vettivelraj Avatar asked Apr 29 '12 06:04

Thenraja Vettivelraj


3 Answers

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
like image 138
Samuel Audet Avatar answered Nov 16 '22 21:11

Samuel Audet


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.

like image 7
danidiaz Avatar answered Nov 16 '22 20:11

danidiaz


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.

like image 4
Paul Johnson Avatar answered Nov 16 '22 21:11

Paul Johnson