Подтвердить что ты не робот

Связь между Java и Haskell

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

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

4b9b3361

Ответ 1

Вызов Haskell из 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


Изменить. Я взял на себя смелость выполнить некоторую микрообъектизацию накладных расходов. Со следующим заголовком C Safe.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

Справедливости ради следует отметить, что также довольно дорого обращаться к JVM...


Обновить: При последних изменениях в JavaCPP теперь пользователи могут обращаться к функции обратного вызова (указатели) по имени из C/С++, что позволяет легко звонить в JVM из Haskell. Например, на основе информации, найденной на странице wiki-страницы

и a 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

Ответ 2

Если вы выбираете подход к серверному процессу Haskell, вы можете использовать библиотеку MessagePack serialization/rpc. Он имеет привязки для Java и Haskell, и привязки Haskell, похоже, хорошо поддерживаются. Найдите msgpack и msgpack-rpc в Hackage.

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

Ответ 3

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

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

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

Ответ 4

TL; DR: используйте шаблон передачи сообщений (например, RPC client-server или peers).

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


Структуры/спецификации RPC

  • gRPC общедоступная вилка Google Protobufs RPC через HTTP/2

  • msgpack-rpc Не включает в себя транспорт.

  • zerorpc ZeroMQ + msgpack. Только имеет реализации Python и Node. Кажется, тоже отказались.

  • XML-RPC Зрелый. Широкая интероперабельность, но также и XML.

  • JSON-RPC Легче отлаживать. Не бинарный протокол, хотя BSON может взломать некоторые библиотеки.


Сериализация

  • Буферы протоколов (protobufs) Для этого существует множество инструментов, чем другие. Он поддерживает члены версии/необязательные данные, которые не требуют перекомпиляции (или нарушения) мира для взаимодействия.

  • msgpack. Симпатичный, простой и эффективный, но он не поддерживает переходы, совместимые с схемами. Это просто немой двоичный кодек. Вероятно, слишком простой и низкоуровневый для практического использования.


Транспорт

  • ZeroMQ Вероятно, самый быстрый, не-Infiniband/FC/10 GbE, -прозрачный транспорт сообщений.

  • Nanomsg Новая, потокобезопасная, UNIX-философия, переосмысливающая ZeroMQ от один из его основателей.

  • HTTP/2 (используется gRPC). Преимуществом здесь является стандарт, который работает внутри и между центрами данных, а также имеет TLS (собственный код gRPC использует BoringSSL, Google "более безопасный" OpenSSL-вилка".

  • MQTT. Когда вам нужно внедрить push-уведомления на миллиард устройств и использовать протокол, читаемый человеком.