C# 在C语言中使用高阶Haskell类型#

C# 在C语言中使用高阶Haskell类型#,c#,haskell,ffi,C#,Haskell,Ffi,如何使用和调用具有来自C#(DLLImport)的高阶类型签名的Haskell函数,例如 C#中对应的类型签名是什么 此外(因为这可能更容易):我如何在C#中使用“未知”Haskell类型,这样我至少可以在C#不知道任何特定类型的情况下传递它们?我需要知道的最重要的功能是传递类型类(如Monad或Arrow) 我已经知道并在C#中使用过,但只适用于一阶函数。我也知道,而且,我没有找到任何文档和示例(针对C#to Haskell方向)。您是否尝试通过导出导出函数?这允许您为函数创建一个更加C-is

如何使用和调用具有来自C#(DLLImport)的高阶类型签名的Haskell函数,例如

C#中对应的类型签名是什么

此外(因为这可能更容易):我如何在C#中使用“未知”Haskell类型,这样我至少可以在C#不知道任何特定类型的情况下传递它们?我需要知道的最重要的功能是传递类型类(如Monad或Arrow)


我已经知道并在C#中使用过,但只适用于一阶函数。我也知道,而且,我没有找到任何文档和示例(针对C#to Haskell方向)。

您是否尝试通过导出导出函数?这允许您为函数创建一个更加C-ish的接口。我怀疑直接从C#调用Haskell函数是否可能。有关更多信息,请参阅文档。(上面的链接)


在做了一些测试之后,我认为通常情况下,不可能通过FFI导出高阶函数和带有类型参数的函数。[需要引用]

好的,多亏了FuzzXL,他为“未知类型”提出了一个解决方案。将数据存储在IO上下文中的HaskellMVar中,并使用一阶函数从C#到Haskell进行通信。至少在简单的情况下,这可能是一个解决方案。

我将在这里详细阐述我对Fuzzxl帖子的评论。
您发布的示例都可以使用
FFI
。一旦使用FFI导出函数,您就可以将程序编译成DLL

net是为了能够很容易地与C、C++、COM等接口而设计的,这意味着一旦你能够将你的函数编译成DLL,你就可以从.NET中轻松调用它。正如我在之前链接到的另一篇文章中提到的,请记住导出函数时指定的调用约定。NET中的标准是
stdcall
,而Haskell
FFI
的(大多数)示例使用
ccall
导出

到目前为止,我发现FFI可以导出的唯一限制是多态类型或未完全应用的类型。e、 g.除种类以外的任何内容(例如,您不能导出
可能
,但可以导出
可能是Int

我已经编写了一个工具,可以自动覆盖和导出示例中的任何函数。它还可以选择生成
unsafe
C#代码,这使得它几乎可以“即插即用”。我之所以选择不安全代码,是因为它更容易处理指针,从而更容易对数据结构进行编组

最后,我将详细介绍该工具如何处理您的示例,以及我计划如何处理多态类型

  • 高阶函数
导出高阶函数时,需要稍微更改函数。高阶参数需要成为的元素。基本上,它们被视为显式函数指针(或c#中的委托),这是命令式语言中通常实现高阶性的方式。
假设我们将
Int
转换为
CInt
则double的类型从

(Int -> Int) -> Int -> Int
进入

FunPtr (CInt -> CInt) -> CInt -> IO CInt
这些类型是为导出的包装函数(在本例中为
double a
)而不是
double
本身生成的。包装函数在原始函数的导出值和预期输入值之间映射。之所以需要IO,是因为构造
FunPtr
不是纯粹的操作。
需要记住的一点是,构造或取消引用
FunPtr
的唯一方法是静态创建导入,该导入指示GHC为此创建存根

foreign import stdcall "wrapper" mkFunPtr  :: (Cint -> CInt) -> IO (FunPtr (CInt -> CInt))
foreign import stdcall "dynamic" dynFunPtr :: FunPtr (CInt -> CInt) -> CInt -> CInt
“wrapper”函数允许我们创建一个
FunPtr
,而“dynamic”
FunPtr
则允许我们尊重一个

在C#中,我们将输入声明为
IntPtr
,然后使用
Marshaller
helper函数创建一个我们可以调用的函数指针,或者使用反向函数从函数指针创建一个
IntPtr

还要记住,作为参数传递给FunPtr的函数的调用约定必须与参数传递给的函数的调用约定相匹配。换句话说,将
&foo
传递到
bar
需要
foo
bar
具有相同的调用约定

  • 用户数据类型
导出用户数据类型实际上非常简单。对于每个需要导出的数据类型,必须为此类型创建一个实例。此实例指定GHC需要的编组信息,以便能够导出/导入此类型。除此之外,您还需要定义类型的
大小
对齐方式
,以及如何向指针读取/写入类型的值。我部分用于此任务(因此文件中有C宏)

newtypes
datatypes
只需一个构造函数就很容易了。由于在构造/销毁这些类型时只有一种可能的选择,因此这些类型将成为平面结构。具有多个构造函数的类型成为一个并集(在C#中,
Layout
属性设置为
Explicit
)。但是,我们还需要包含一个枚举来标识正在使用的构造

通常,数据类型
Single
定义为

data Single = Single  { sint   ::  Int
                      , schar  ::  Char
                      }
创建以下可存储的
实例

instance Storable Single where
    sizeOf    _ = 8
    alignment _ = #alignment Single_t

    poke ptr (Single a1 a2) = do
        a1x <- toNative a1 :: IO CInt
        (#poke Single_t, sint) ptr a1x
        a2x <- toNative a2 :: IO CWchar
        (#poke Single_t, schar) ptr a2x

    peek ptr = do 
        a1' <- (#peek Single_t, sint) ptr :: IO CInt
        a2' <- (#peek Single_t, schar) ptr :: IO CWchar
        x1 <- fromNative a1' :: IO Int
        x2 <- fromNative a2' :: IO Char
        return $ Single x1 x2
函数
foo::Int->Single
将导出为
foo::CInt->Ptr Single
而具有多个构造函数的数据类型

data Multi  = Demi  {  mints    ::  [Int]
                    ,  mstring  ::  String
                    }
            | Semi  {  semi :: [Single]
                    }
生成以下C代码:

enum ListMulti {cMultiDemi, cMultiSemi};

typedef struct Multi Multi_t;
typedef struct Demi Demi_t;
typedef struct Semi Semi_t;

struct Multi {
    enum ListMulti tag;
    union MultiUnion* elt;
} ;

struct Demi {
     int* mints;
     int mints_Size;
     wchar_t* mstring;
} ;

struct Semi {
     Single_t** semi;
     int semi_Size;
} ;

union MultiUnion {
    struct Demi var_Demi;
    struct Semi var_Semi;
} ;
Storable
实例相对简单,应该遵循ea
typedef struct Single Single_t;

struct Single {
     int sint;
     wchar_t schar;
} ;
data Multi  = Demi  {  mints    ::  [Int]
                    ,  mstring  ::  String
                    }
            | Semi  {  semi :: [Single]
                    }
enum ListMulti {cMultiDemi, cMultiSemi};

typedef struct Multi Multi_t;
typedef struct Demi Demi_t;
typedef struct Semi Semi_t;

struct Multi {
    enum ListMulti tag;
    union MultiUnion* elt;
} ;

struct Demi {
     int* mints;
     int mints_Size;
     wchar_t* mstring;
} ;

struct Semi {
     Single_t** semi;
     int semi_Size;
} ;

union MultiUnion {
    struct Demi var_Demi;
    struct Semi var_Semi;
} ;
instance Storable Int => Storable (Maybe Int) where