C#和Haskell之间的通信

时间:2016-06-30 09:14:18

标签: c# json haskell dll

我有这个Haskell代码:

module RectangleMover where

data Point = Point Int Int deriving (Show)
movePoint :: Point -> Int -> Int -> Point
movePoint (Point x y) dx dy = Point (x + dx) (y + dy)

data Rectangle = Rectangle { corner :: Point, width :: Int, height :: Int } deriving (Show)
move :: Rectangle -> Int -> Int -> Rectangle
move r@(Rectangle {corner=c}) dx dy = r { corner = movePoint c dx dy }

p = Point 1 2
hsRec = Rectangle p 10 20

这个等效的C#代码是:

class Point
{
    private int x;

    private int y;

    public Point(int x, int y)
    {
        this.x = x;
        this.y = y;
    }

    public void Move(int dx, int dy)
    {
        this.x += dx;
        this.y += dy;
    }
}

class Rectangle
{
    private Point point;

    private int width;

    private int height;

    public Rectangle(Point p, int width, int height)
    {
        this.point = p;
        this.width = width;
        this.height = height;
    }

    public void Move(int dx, int dy)
    {
        this.point.Move(dx, dy);
    }
}

Point p = new Point(1,2);
Rectangle csRec = new Rectangle(p, 10, 20);

我现在的问题是如何将“实例”hsRec Haskell传递给C#,将csRec从C#传递给Haskell。在这种情况下,常见的方法是使用FFI从Haskell代码创建DLL,并从C#调用此DLL。反过来说,从C#创建一个DLL并从Haskell调用此DLL。

要从Haskell导出,这是一个带整数的简单示例:

{-# LANGUAGE ForeignFunctionInterface #-}

module Add () where

import Foreign.C.Types

add :: CInt -> CInt -> CInt
add x y = x + y

foreign export ccall add :: CInt -> CInt -> CInt

但是如何在这些语言之间传递更复杂的类型呢?在这种情况下传递一个矩形类型的对象。 是否可以将对象转换为JSON或XML字符串并将其传递给另一种语言?

2 个答案:

答案 0 :(得分:3)

您的选择是:

  • 手动将所有C#/ Haskell结构转换为普通C结构。 (性能高,编写复杂,难以调试)。

  • 手动将C#/ Haskell结构序列化/反序列化为JSON / XML / YAML / CSV /等等。 (性能更低,更容易调试。)

  • 对于每个数据结构,手动编写和公开知道如何从中获取普通数据的函数。 (例如,如果你有一个Haskell Map,导出函数一次获取一个键,以一种微不足道的方式让C理解,然后从C#端包装它。)

AFAIK,没有办法"直接"或者"自动"使C#理解Haskell对象的布局,反之亦然。

答案 1 :(得分:2)

高效的方法是使用C FFI在Haskell和C#之间创建桥接。这将是一项很多工作。

另一种方法是使用像MessagePack这样的通用序列化协议。这是一个Haskell库,通过MessagePack可以很容易地与其他语言进行通信:https://github.com/nh2/call-haskell-from-anything

显然,这种技术有很高的呼叫能力。开销,但它将比使用REST API和通过网络堆栈(当然这是另一种选择)快得多。只要调用开销不是你的瓶颈,那么这是一个非常合理的方法。