As far as both languages are concerned, you can basically pretend you're trying to interface with C code.
This is a complex topic, so rather than try to explain all of it, I will focus on making a simple example that you can build on using the resources linked below.
First, you need to write wrappers for your Haskell functions that use types from the
Foreign.C.*
modules instead of the usual haskell types.CInt
instead ofInt
,CString
instead ofString
, etc. This is the most complicated step, especially when you have to deal with user-defined types.You also have to write
foreign export
declarations for those functions using theForeignFunctionInterface
extension.{-# LANGUAGE ForeignFunctionInterface #-} module Foo where import Foreign.C.String import Foreign.C.Types foreign export ccall foo :: CString -> IO CInt foo :: CString -> IO CInt foo c_str = do str <- peekCString c_str result <- hs_foo str return $ fromIntegral result hs_foo :: String -> IO Int hs_foo str = do putStrLn $ "Hello, " ++ str return (length str + 42)
Then, when compiling, you tell GHC to make a shared library:
$ ghc -O2 --make -no-hs-main -optl '-shared' -o Foo.so Foo.hs
From the C# side, in addition to importing the function you want to call, you also have to import
hs_init()
and call it to initialize the runtime system before you can call any Haskell functions. You should also callhs_exit()
when you're done.using System; using System.Runtime.InteropServices; namespace Foo { class MainClass { [DllImport("Foo.so", CallingConvention = CallingConvention.Cdecl)] private static extern void hs_init(IntPtr argc, IntPtr argv); [DllImport("Foo.so", CallingConvention = CallingConvention.Cdecl)] private static extern void hs_exit(); [DllImport("Foo.so", CallingConvention = CallingConvention.Cdecl)] private static extern int foo(string str); public static void Main(string[] args) { Console.WriteLine("Initializing runtime..."); hs_init(IntPtr.Zero, IntPtr.Zero); try { Console.WriteLine("Calling to Haskell..."); int result = foo("C#"); Console.WriteLine("Got result: {0}", result); } finally { Console.WriteLine("Exiting runtime..."); hs_exit(); } } } }
Now we compile and run:
$ mcs -unsafe Foo.cs $ LD_LIBRARY_PATH=. mono Foo.exe Initializing runtime... Calling to Haskell... Hello, C# Got result: 44 Exiting runtime...
It works!
Resources: