Communication between Java and Haskell

前端 未结 4 1697
没有蜡笔的小新
没有蜡笔的小新 2020-12-02 23:25

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 nee

4条回答
  •  慢半拍i
    慢半拍i (楼主)
    2020-12-03 00:01

    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={"","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={"","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
    

提交回复
热议问题