( ゚∀゚)o彡°O'PyCaml

( ゚∀゚)o彡°O'PyCaml

リリースして無いけど公開してるよ

( ゚∀゚)o彡°O'PyCaml は OCaml から Python/C API を使って Python ライブラリを呼び出すインターフェースです。大体出来ました。使いながらチューンしていきます。
Bitbucket で公開してます: http://bitbucket.org/camlspotter/opycaml
必要なライブラリやツールが豊富なので、非常に敷居の高い物になっています。コンパイルできるものならしてみやがれです:

  • OCaml 3.12.0 最新版 OCaml が必要です。3.11.2 でさえ門前払いです。きびしーです。
  • Findlib : ライブラリ使いまわす便利さのために敢えてツールに依存しています。
  • CamlIDL : いちいち FFI を手で書くのは時間の無駄なので CamlIDL を使って楽をしています。
  • Python 2.x (>= 2.6) library and header : 当然 Python が必要です
  • OMake : Makefile 長いの書くの面倒なのでビルドツールも必要です。
  • Perl : なぜか Perl も必須

なんで ( ゚∀゚)o彡°O'PyCaml ?

PythonOCaml から呼ぶ、そしてその逆は既に PyCaml というナイスなライブラリが提供しているのですが、残念ながら、

  • バグっている
  • Segfault する
  • クラッシュする
  • Fix する気が起きないソース
  • 作者もメンテしないソース
  • 他の人もまともにメンテして無いソース

なので、正直爺さんでも、だましだましでないと使えません。困ったことです。しかし、これ使えるよなどと前回の Python Hackerthon で宣伝してしまいました。もちろん誰も使って無いとは思いますが心が痛みます。そこで ( ゚∀゚)o彡°O'PyCaml を書いています。

( ゚∀゚)o彡°O'PyCaml の特徴

今時のプログラミングスタイルに合わせたモジュール空間を提供

PyHogehoge_Mogemoge() という C 関数は、module Hogehoge の mogemoge という OCaml 関数にマップされます。open Opycaml.Api すれば、Hogehoge.mogemoge で使える。とっても自然。Hogehoge が何度も打つのが大変なら OCaml 3.12.0 の local module open が使えます。
万が一 ( ゚∀゚)o彡°O'PyCaml をコンパイルできた人は autoapi.ml に自動生成された関数群がちゃんとモジュールに仕分けされているのが見れるでしょう。api.ml は autoapi.ml をちょっとラップして人間様に使いやすいようにしてあります。

CamlIDL とキチャナイ Perl ハックで、Python/C API の関数の型を書けばそれだけでどんどん拡張可能

たとえば、

[new] PyObject* PyObject_Call(PyCallableObject *callable_object, PyTupleObject *args, [option] PyDictObject *kw);

という Python/C API の関数ヘッダになにやら怪しげな [new] とか [option] とかのアトリビュートをつけると、CamlIDL と殺伐とした Perl スクリプトがあーたらこーたらして、自動的に Python/C API の関数を OCaml にいー感じにマップしてくれます。これで、Python C/API の7割の関数は自動的に OCaml 関数として使えます。自分の欲しい関数が入ってなければ、このヘッダ行を *.idl.in ファイルに加えるだけ! (もしあなたが7割側なら。)何となく拡張していけるよ。

Python の標準クラス階層を OCaml の polymorphic variant type の subtyping を使って自然に表現、静的に型をある程度チェック可能。

例えば、Dict クラスは Mapping のサブクラスで、 Mapping は Object のサブクラスですけど、この関係が OCaml の静的型システムの上で表現されています。これを利用して、Mapping のオブジェクトを取る関数に間違って整数のオブジェクトを渡したり、とか、間抜けな事が起こらないようになってます。もちろん動的な Python クラス型検査(coercion)もあるよ。(今のところ、標準クラス階層のみ)
例えば、

  external getDict  : [>_Module] t -> _Dict t
	= "camlidl_auto_PyModule_GetDict"

は PyModule_GetDict の OCaml 関数、Module.getDict の宣言。 [>_Module] t は引数として、PyModuleObject * かそのサブクラスを受け取れる、_Dict t は、結果の返り値が PyDictObject * であることを示しています。

Segfault 激減。もちろん Python/C API の約束を守らずに適当に呼ぶとクラッシュする。でも PyCaml の様な、ほんの少し型が合ってなかった => あぼーん は無いよ。

PyCaml は NULL もごく普通の Python object として OCaml 側で使えていたのですが、これのおかげと、全く NULL チェックが無いのと、reference counting のバグと、他いろんなバグのお陰で何かあるとすぐ Segfault するひ弱な坊やでした。それと比べるととても安心して使えます。エラーは OCaml の例外として報告されるよ。

Findlib と OMake で楽々コンパイル+リンク

example ディレクトリに、( ゚∀゚)o彡°O'PyCaml をインストールしてからどうやって使うのか、簡単な例をおいときました。OMake と Findlib を使って凄く簡単に書けます:

USE_OCAMLFIND=true

OCAMLPACKS = opycaml

.DEFAULT: $(OCamlProgram test, test)

.PHONY: clean
clean:
  rm -f $(filter-proper-targets $(ls R, .))

これで、 test.ml を ( ゚∀゚)o彡°O'PyCaml を使ってコンパイル、必要なライブラリをリンクして、test.opt (native code) や test.run (byte code) プログラムが作成されます。もうコンパイルフラッグがあーたらーこーたら言う必要も無い! OMake ほんまえーわー。

例えば

string モジュールを import して、capitalize(lowercase) をプリントするにはこうします:

open Opycaml.Api

let _ =
  Base.initialize ();

  let string = Import.importModule "string" in (* from string import * *)
  let dict = Module.getDict string in
  let lowercase = Dict.getItemString dict "lowercase" in
  let capitalize = Dict.getItemString dict "capitalize" in

  let res = Object.callObject (Callable.coerce capitalize) [lowercase] in

  prerr_endline (String.asString (String.coerce res));

  Base.finalize ()
;;

ぐへっ、長いね!

ぼちぼち拡充してくよ

まーそういうことです。OMake, Findlib, CamlIDL, OCaml/C interface(FFI), Polymorphic variant typing とか一つ一つ話をしだすとそれはそれで面白いですが、まあ、これもぼちぼちですわ。そういうのがこのパッケージにギュッと詰まっている訳で、そういう意味でも書いていて面白いです。