I want to use a Haskell function with the following type ::
string -> string
from a C# program.
I want to use hs-dotnet to bridge both worlds. The author claim that it's possible, but provide no sample of this case. The only samples provided are the one to use .NET from Haskell.
Is there a sample of this use, or how to use it? (I used .NET Reflector on the bridging assembly, but I didn't understand a thing.)
While your way works, it's worth noting that the dificulties you encountered were of your own doing unfortunately (and not a bug in GHC) :( (The following assumes you used the GHC documentation when building the DLL and have your RTS loading in DLL main).
For the first part, the memory allocation issues you present, there's a much easier C# native way of handling this, which is unsafe code. Any memory allocated in unsafe code will be allocated outside the managed heap. So this would negate the need for C trickery.
The second part is the use of the LoadLibrary in C#. The reason P/Invoke can't find your export is quite simple: in your Haskell code you declared the export statement using
ccall
, while in .NET the standard naming convention isstdcall
, which is also the standard for Win32 API calls.stdcall
andccall
have different name manglings and resposibilities in term of argument cleanup.In particular, GHC/GCC will have exported "wEval" while .NET by default would be looking for "_wEval@4". Now that's quite easy to fix, just add CallingConvention = CallingConvention.Cdecl.
But using this calling convention the caller needs to clean up the stack. So you would need extra work. Now assuming you're only going to use this on Windows, just export your Haskell function as
stdcall
. This makes your .NET code simpler and makesalmost correct.
What's correct would be for example
No more need for loadLibrary or the like. And to get a managed string just use
for instance.
One would think that
should work too, but It doesn't since the Marshaller expects the String to have been allocated with CoTaskMemAlloc and will call CoTaskMemFree on it and crash.
If you want to stay completely in managed land, you could always do
and then it can be used as
Tool is available here http://hackage.haskell.org/package/Hs2lib-0.4.8
Update : There's somewhat of a big gotcha that I've recently discovered. We have to remember that the String type in .NET is immutable. So when the marshaller sends it to out Haskell code, the CWString we get there is a copy of the original. We have to free this. When GC is performed in C# it won't affect the the CWString, which is a copy.
The problem however is that when we free it in the Haskell code we can't use freeCWString. The pointer was not allocated with C (msvcrt.dll)'s alloc. There are three ways (that I know of) to solve this.
You can certainly call Haskell from C at least -- you use "foreign export" in the Haskell file, and GHC generates a C header which you can then import and use to call into Haskell from C.
I've not seen this done for the .NET bindings -- so I think it is best to ask both the author - Sigbjorn - and on haskell-cafe@ for examples.
If you want Haskell on .NET, just use F#.
Just as an update, I've solved the problem by making an haskell DLL and bridging the two worlds that way.
If you want to take the same path, be sure to use
::CoTaskMemAlloc
to allocate data for the .net world. Another gotcha is the use of LoadLibrary/GetProcAdress, for some unknown reason, imports doesn't work automatically the way they're supposed to be. A more in depth article to help calling haskell from .net.