laclefblog logo

Sun, 16 Nov 2008

Haskellで作成した関数をPythonから使う

漸く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でもポインタも扱えるので、もう少し手の込んだことも出来る(はず)。