漸くHaskellに慣れてきたが、Pythonの方が使い勝手が良いと感じることに変わりはない。
互いの良さを取り、上手く使い分けるために、HaskellとPythonのFII(Foreign Function Interface)を用いる。
Haskell/GHCでForeign.Cを用いてC共有ライブラリを作成し、Pythonからctypesで使用する。
まず、Haskellで関数を格納したモジュールを書く(hstopy.hs)。
module Htop where import CString fact :: Int -> Int fact n = if n == 1 then 1 else (n * fact (n - 1)) factorial :: Int -> IO Int factorial n = return (fact n) mystring :: IO CString mystring = newCString "hello haskell world!" foreign export ccall factorial :: Int -> IO Int foreign export ccall mystring :: IO CString
FIIの性質上、色々をIOモナドで包む必要がある。
次に、ランタイム初期化と脱初期化のためのエントリィポイント用コードを書く(必要最小限、hstopy_init.c)。
static void __attribute__ ((constructor)) mylib_init(void)
{
hs_init(0, 0);
}
GHCでコンパイルする。
$ ghc -c hstopy.hs -fglasgow-exts $ ls hstopy.hi hstopy.hs hstopy.o hstopy_init.c hstopy_stub.c hstopy_stub.h hstopy_stub.o
GHCで共有ライブラリを作成する。
$ ghc -package unix -optl "-shared" -o libhstopy.so hstopy_init.c hstopy.o hstopy_stub.o # -package unix は今回は不要? $ ls hstopy.hi hstopy.hs hstopy.o hstopy_init.c hstopy_init.o hstopy_stub.c hstopy_stub.h hstopy_stub.o libhstopy.so
ctypesで共有ライブラリの関数を用いるために、関数ラッパを書く(hstopy.py)。
#!/usr/bin/python import ctypes _lib = ctypes.CDLL( './libhstopy.so') _lib.factorial.argtypes = [ctypes.c_int] _lib.factorial.restype = ctypes.c_int _lib.mystring.argtypes = [] _lib.mystring.restype = ctypes.c_char_p factorial = _lib.factorial mystring = _lib.mystring
Pythonから使用する。
$ python Python 2.5.2 (r252:60911, Oct 5 2008, 19:24:49) [GCC 4.3.2] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import hstopy >>> hstopy.mystring() 'hello haskell world!' >>> hstopy.factorial(4) 24 >>> hstopy.factorial(5) 120
Foreign.Cでもctypesでもポインタも扱えるので、もう少し手の込んだことも出来る(はず)。