在C#中使用高阶Haskell类型HowtouseandcallHaskellfunctionsusinghigher-ordertypesignaturesinC#(DLLImport),例如...double::(Int->Int)->Int->Int--高阶函数typeClassFunc::...->MaybeInt--类型类数据MyData=Foo|Bar--用户数据类型dataFunc::...-MyDataC#中相应的类型签名是什么?[DllImport("libHSDLLTest")]privatestaticextern???富(???);另外(因为它可能更容易):如何在C#中使用“未知的”Haskell类型,这样我至少可以在不知道任何特定类型的C#的情况下传递它们?我需要知道的最重要的特性是传递类型类(如Monad或Arrow)。我已经知道如何将Haskell库编译为DLL并在C#中使用它,但仅用于一阶函数。我也知道Stackoverflow-在.NET中调用Haskell函数,为什么GHC不能与.NET和hs-dotnet一起工作,我没有找到任何文档和示例(对于C#到Haskell的方向)。我将在这里详细说明我对FUZxxlpost的评论。您发布的所有示例都适用于FFI。使用FFI导出函数后,您可以将编写的程序编译成DLL。.NET旨在能够轻松地与C、C++、COM等接口。这意味着一旦您能够将函数编译为DLL,就(相对)容易地从.NET调用它。正如我之前在您发布的其他帖子中提到的那样,请记住导出函数时指定的调用约定。.NET中的标准是stdcall,而(大部分)使用ccall导出Haskell的FFIccall。到目前为止,我发现对FFI可以导出的内容的唯一限制是多态类型或未完全应用的类型。例如,除了kind*之外的任何东西(您不能导出Maybe,但可以导出MaybeInt)。我编写了一个工具Hs2lib,它可以自动覆盖和导出示例中的任何函数。它还可以生成不安全的C#代码,使其非常“即插即用”。我选择不安全代码的原因是因为它更容易处理指针,而指针又更容易编组数据结构。为了完整起见,我将详细说明该工具如何处理您的示例以及我打算如何处理多态类型。导出高阶函数时,函数需要稍微改动一下。高阶参数需要是FunPtr的元素。基本上它们被视为显式函数指针(或c#中的委托),这是通常在命令式语言中完成的高阶排序。假设我们将Int转为CInt,然后转为double类型(Int->Int)->Int->Int转为FunPtr(CInt->CInt)->CInt->IOCInt这些类型是针对wrapperfunctions的(在this中对于doubleA的情况),导出的不是替身本身。包装函数映射原始函数的导出值和预期输入值。需要IO是因为构造FunPtr不是纯粹的操作。要记住的一件事是,构建或取消引用FunPtr的唯一方法是静态创建导入,指示GHC为其创建存根。国外导入stdcall“包装器”mkFunPtr::(Cint->CInt)->IO(FunPtr(CInt->CInt))国外导入stdcall“动态”dynFunPtr::FunPtr(CInt->CInt)->CInt->CInt““包装器”函数允许我们创建一个FunPtr,而“动态”FunPtr允许我们单独执行。在C#中,我们将输入声明为IntPtr,然后使用Marshaller辅助函数Marshal.GetDelegateForFunctionPointer创建一个函数指针我们可以调用,或者使用逆函数从函数指针创建IntPtr。还要记住,作为参数传递给FunPtr的函数的调用约定必须与传递参数的函数的调用约定相匹配。换句话说,传递&footobar要求foo和bar具有相同的调用约定。导出用户数据类型其实很简单。对于需要导出的每种数据类型,必须创建该类型的可存储实例。此实例指定GHC需要能够导出/导入此类型的编组信息。除此之外,还需要定义类型的大小和对齐方式,以及如何读/写指针类型的值。我部分使用Hsc2hs来完成这项任务(因此文件中有C宏)。只有一个构造函数的新类型或数据类型很容易。这些成为平面结构,因为在构造/销毁这些类型时只有一种可能的选择。具有多个构造函数的类型成为联合(在C#中Layout属性设置为Explicit的结构)。但是,我们还需要包括一个枚举来标识正在使用的构造。一般来说,数据类型Single定义为dataSingle=Single{sint::Int,schar::Char}创建如下Storable实例instanceStorableSinglewheresizeOf_=8alignment_=#alignmentSingle_tpokeptr(Singlea1a2)=doa1x和CstructtypedefstructSingleSingle_t;结构单{intsint;wchar_tschar;};functionfoo::Int->Single将导出为foo::CInt->PtrSingle尽管数据类型有多个构造函数dataMulti=Demi{mints::[Int],mstring::String}|Semi{semi::[Single]}生成以下C代码:enumListMulti{cMultiDemi,cMultiSemi};typedefstructMultiMulti_t;typedefstructDemiDemi_t;typedefstructSemiSemi_t;structMulti{枚举ListMulti标签;联合MultiUnion*elt;};structDemi{int*薄荷糖;intmints_Size;wchar_t*字符串;};黛咪var_Demi;结构半var_Semi;};可存储实例相对简单,从C结构定义开始应该更容易。我的依赖跟踪器为Int类型的类型发出MaybeInt并且Maybe依赖于Int和Maybe类型。这意味着,当生成MaybeInt的Storable实例时,header看起来像instanceStorableInt=>Storable(MaybeInt)where也就是说,只要应用程序的参数有一个Storable实例,也可以导出类型本身。由于Maybea被定义为具有多态参数Justa,因此在创建结构时会丢失一些类型信息。该结构将包含一个void*参数,您必须手动将其转换为正确的类型。在我看来太麻烦的替代方案是创建专门的结构。例如结构MaybeInt。然而,可以从普通模块生成的专用结构的数量会以这种方式迅速爆炸。(稍后可能会添加为标志)。为了减少这种信息丢失,我的工具将它为该函数找到的所有Haddock文档导出为生成的包含中的注释。它还会将原始的Haskell类型签名放在注释中。IDE然后将这些作为其Intellisense(代码竞赛)的一部分呈现。与所有这些示例一样,我省略了代码的.NET端,如果您有兴趣,可以只查看Hs2lib的输出。还有一些其他类型需要特殊处理。特别是列表和元组。列表需要传递它从中编组的数组的大小,因为我们正在与一种非托管语言交互,其中数组的大小不是隐式已知的。相反,当我们返回一个列表时,我们还需要返回列表的大小。元组是特殊的构建类型,为了导出它们,我们必须首先将它们映射到“正常”数据类型,然后再导出它们。在该工具中,这将完成最多8元组。多态类型的问题,例如map::(a->b)->[a]->[b]是a和b的大小未知。也就是说,没有办法为参数和返回值保留空间,因为我们不知道它们是什么。我计划通过允许您为a和b指定可能的值并为这些类型创建专门的包装函数来支持这一点。在另一种情况下,在命令式语言中,我会使用重载来向用户呈现您选择的类型。至于类,Haskell的开放世界假设通常是一个问题(例如可以随时添加实例)。但是,在编译时,只有静态已知的实例列表可用。我打算提供一个选项,以使用这些列表自动导出尽可能多的专用实例。例如,export(+)在编译时为所有已知的Num实例(例如Int、Double等)导出一个专用函数。该工具也非常值得信赖。由于我无法真正检查代码的纯度,所以我始终相信程序员是诚实的。例如,您不会将具有副作用的函数传递给需要纯函数的函数。说实话,并将高阶参数标记为不可避免的问题。我希望这会有所帮助,我希望不会花太长时间。更新:我最近发现了一些大问题。我们必须记住,.NET中的String类型是不可变的。所以当编组器将它发送给Haskell代码时,我们得到的CWString是原始的副本。我们必须释放它。当GC在C#中完成时,它不会影响CWString,它是一个副本。但问题是,当我们在Haskell代码中释放freeCWString时,我们不能使用它。指针未由C(msvcrt.dll)的分配分配。有三种方法(据我所知)可以解决这个问题。您是否尝试过通过FFI导出功能?这允许您为函数创建更多C-ish接口。我怀疑是否可以直接从C#调用Haskell函数。有关更多信息,请参阅文档。(上面的链接)。经过一些测试,我认为一般不能通过FFI导出高阶函数和带类型参数的函数。[需要引用]好的,感谢FUZxxl提出了“未知类型”的解决方案。将数据存储在IO上下文中的HaskellMVar中,并使用一阶函数从C#传递到Haskell。至少对于简单的情况,这可能是解决方案。以上就是C#学习教程:Usinghigh-levelHaskelltypesinC#的全部内容分享。如果对你有用,需要进一步了解C#学习教程,希望大家多加关注---本文收集自网络,不代表侵权,如涉及侵权,请点击有权联系管理员删除。如需转载请注明出处:
