Windows Dev. Site

Haskell from C#

Haskell from Excel という過去の投稿で、C++からのHaskell呼び出しをやりましたが、C#から直接できることを以下のサイトで知り、テストしてみました。

Calling Haskell from C#

http://stackoverflow.com/questions/16615641/calling-haskell-from-c-sharp

このサイトでは、Monoで実行していますが、Windowsでテストしました。
また、Stringを戻り値とする関数が使いたかったので、これを追加しています。

環境: GHC ver 7.6.3, VisualStudio 2010 / Windows 7
Foo.hs

module Foo where

import Foreign.C.String
import Foreign.C.Types
import Data.List.Split

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 str
	return (length str)

hstest :: CString -> IO CString
hstest c_str = do
	str <- peekCString c_str
	let s = concat $ fmap cadd $ chunksOf 1 str
	newCString s

cadd :: String -> String
cadd c = do
	c ++ " "

foreign export ccall foo :: CString -> IO CInt
foreign export ccall hstest :: CString -> IO CString

ghc -no-hs-main -shared -o Foo.so Foo.hs

hstestで、C文字列をHaskell文字列に変換し、文字列操作をして、またC文字列に戻しています。

次にC#側ですが、VisualStudioのC#コンソールアプリケーションプロジェクトで作成します。
Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace csConsoleHaskell01
{
	class Program
	{
		[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);

		[DllImport("Foo.so", CallingConvention = CallingConvention.Cdecl)]
		private static extern IntPtr hstest(string str);

		static void Main(string[] args)
		{
			hs_init(IntPtr.Zero, IntPtr.Zero);

			int result = foo("Hello!");
			Console.WriteLine("Length : {0}", result);

			IntPtr r = hstest("Hello_Haskell_String");
			string s = Marshal.PtrToStringAnsi(r);
			Console.WriteLine(s);

			hs_exit();
		}
	}
}

intを戻すときは、そのままでしたが、stringは上のようにポインタを取得してから変換する必要があるようです。(戻り値stringは不可)
Haskell Foo.soをcsConsoleHaskell01\bin\Debugディレクトリにおいて、csConsoleHaskell01.exeをコマンドプロンプトで実行します。

Hello!
Length : 6
H e l l o _ H a s k e l l _ S t r i n g

このように表示されます。
.NetとHaskellを結びつけるには、HaskellのFFI(ForeignFunctionInterface)を使ってC++/CLIとリンクして、さらに.Netマネージコードと連携しなくてはいけないと思っていましたが、このように簡単にできてしまうのですね。C#-Haskellができると、ここからいろいろと派生できそうです。