c#和Haskell之间的通信
本文关键字:通信 之间 Haskell | 更新日期: 2023-09-27 17:53:32
我有这个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字符串并将其传递给另一种语言?
你的选择是:
-
手动将所有c#/Haskell结构转换为普通C结构。(高性能,编写复杂,调试困难)
-
手动序列化/反序列化你的c#/Haskell结构为JSON/XML/YAML/CSV/任何。(较低的性能,大大容易调试)
-
对于每个数据结构,手动编写并公开知道如何从中获取琐碎数据的函数。(例如,如果你有一个Haskell
Map
,导出函数一次获取一个键,以一种足以让C理解的方式,然后从c#端包装。)
老实说,没有办法"直接"或"自动"让c#理解Haskell对象的布局,反之亦然。
最有效的方法是使用C FFI在Haskell和c#之间创建一个桥梁。那将是大量的工作。
另一种方法是使用通用的序列化协议,如MessagePack。这里有一个Haskell库,它可以很容易地通过MessagePack与其他语言进行通信:https://github.com/nh2/call-haskell-from-anything
显然,这种技术有很高的"调用"开销,但它比使用REST API和通过网络堆栈(当然这是另一种选择)要快得多。只要调用开销不是瓶颈,那么这是一个非常合理的方法。