Вопрос по java, haskell, java-native-interface – Связь между Java и Haskell

20

Я погуглил и получил несколько ответов, что связь между Java и Haskell может осуществляться с помощью GCJNI (теперь сайт закрыт) и LambdaVM. Чтобы использовать LambdaVM / GCJNI, нужно ли мне загружать какие-либо инструменты сборки? Где я могу узнать о них больше, так как я не нахожу много ресурсов в Интернете?

Я хочу разработать приложение, которое взаимодействует между Java и Haskell (где я получу входные данные от Java, передам его на Haskell, обработаю там и верну результат обратно в Java). Это то, что я хочу сделать. Пожалуйста, помогите мне...

Я не знаю, может ли это быть вариант в вашем случае, но есть очень "haskellish" язык под названием Фреге на JVM:code.google.com/p/frege Landei
Так как вы не приняли ни один из ответов ниже, что еще вам нужно знать? Samuel Audet

Ваш Ответ

4   ответа
1

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

Why? Это безопаснее, масштабируемо, гибко и отлаживаемо. Вызов в FFI будет хрупким и сложным для тестирования.

RPC frameworks/specifications

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

unofficial Haskell gRPC bindings

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.

7

Если вы выберете подход на сервере Haskell, вы можете использоватьMessagePack Библиотека сериализации / RPC. У него есть привязки как для Java, так и для Haskell, и привязки на Haskell, похоже, хорошо поддерживаются. Ищуmsgpack а такжеmsgpack-rpc на Hackage.

Вот забавный пример взаимодействия Java / Haskell с использованием MessagePack:Java-сервер, Клиент Haskell (ссылки идут на GitHub). Общение в противоположном направлении от того, что вы хотите, хотя.

@ Даниэль Спасибо за руководство !!! Thenraja Vettivelraj
4

Это зависит от того, как вы хотите, чтобы они общались. Иметь код Java и Haskell, работающий изначально в одном и том же процессе и обменивающийся данными в памяти через их соответствующие FFI, является огромной проблемой, не в последнюю очередь потому, что у вас есть два GC, борющихся за данные, и два компилятора, каждый из которых имеет свои собственные идеи представления различные типы данных. Скомпилировать Haskell под JVM также сложно, потому что у JVM (в настоящее время) нет концепции замыканий.

Конечно, это можно сделать, но переход от демонстратора к промышленному инструменту требует огромных усилий. Насколько я понимаю, инструменты, о которых вы говорите, никогда не выходили за рамки демонстрации.

Более простое, хотя и менее изящное решение, - написать свою программу на Haskell как серверный процесс, который отправляет данные через сокеты из Java. Если производительность и объем не слишком высоки, то кодирование в JSON, вероятно, будет простым, поскольку обе стороны имеют библиотеки для его поддержки.

@ Пол Джонсон Спасибо за руководство !! Thenraja Vettivelraj
Исключение хвостовых вызовов можно получить из манипулятора кода, такого как Kilim, но в целом это не очень удобно.github.com/kilim/kilim/blob/master/docs/IFAQ.txt
это не столько отсутствие закрытия JVM. Многие языки JVM имеют замыкания, технически то же самое делает Java с внутренними классами, а в Java 8 предлагается элегантная реализация лямбд. Проблема в том, что 1. нет вызовов хвоста 2. распределитель / GC не оптимален для функциональных языков (код на Haskell выделяет намного больше небольших объектов с коротким сроком службы, чем в Java). Еще Фреге и т. Д. Показывают, что это возможно.
34

Звонок на Хаскелл из C выглядит довольно просто, и, следовательно, также может быть легко вызван из Java сJavaCPP, Например, чтобы позвонитьfibonacci_hs() функция из примера кода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

мы можем сделать это так из 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);
    }
}

Под Linux процедура компиляции выглядит следующим образом:

$ 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

И программа работает нормально с обычнымjava команда:

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

EditЯ позволил себе провести микробенчмаркинг вызовов. Со следующим заголовочным файлом CSafe.h:

inline int fibonacci_c(int n) {
    return n < 2 ? n : fibonacci_c(n - 1) + fibonacci_c(n - 2);
}

следующий класс Java:

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");
    }
}

выводит это с процессором Intel Core i7-3632QM @ 2,20 ГГц, Fedora 20 x86_64, GCC 4.8.3, GHC 7.6.3 и OpenJDK 8:

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

Справедливости ради стоит отметить, что звонить по телефону тоже довольно дорогоinto JVM также ...

Update: With recent changes to JavaCPPтеперь пользователи могут получать доступ к функциям обратного вызова (указателям) по имени из C / C ++, что позволяет легко вызывать JVM из Haskell. Например, на основе информации, найденной наwiki page regarding Haskell's FFIсо следующим кодом в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

иfibonacci функция, определенная в Java следующим образом:

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);
        }
    }
}

мы можем собрать под Linux x86_64 что-то вроде:

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

и программа выполняется правильно, выдавая такой вывод:

$ ./Main
267914296
Я полностью пытаюсь это ...
@Vagif HJVM - инструмент, который вы ищете:hackage.haskell.org/package/HJVM-0.1
@Vagif Кстати, JavaCPP поддерживает функции обратного вызова C (FunctionPointer) и Haskell FFI также поддерживает указатели на функции C (FunPtr), так что Haskell тоже может вызывать Java. Если вы ищете что-то подобное, дайте мне знать.
@ Самуэль Одет Спасибо за руководство !!! Thenraja Vettivelraj
Теперь, если только кто-то привел пример в обратном направлении (haskell -> java)

Похожие вопросы