このままでいいか

アニメとか,プログラミングとか,研究とか,思ったことをだらだらと書く感じで

ABCL(Armed Bear CommonLisp)でJavaの静的メソッド呼び出し

こんにちは,お久しぶりです,とまぶです

もうすぐ関西Lispユーザ会の第2回勉強会がありますね.
最近は研究なりなんなりがバタバタしていてなかなか技術系のアウトプット的なことができませんでした...

そんな中でも,なんとかまとめられそうな話が1つだけあったので書いておきましょう.

ところで,皆さんはどの処理系をお使いになられているのでしょうか.

無難にSBCLでしょうか,それともちょっと頑張ってACLやLispworks?
CCL,CLISPなんて方もいらっしゃるでしょうか.

僕が知らないだけで,ECLやGCLを使っている人もいたりするんでしょうね.

僕自身は普段はCCLを使っていますが,今回はタイトルの通り,ABCLを使ってみている話です.

ABCLとは?

こんな記事を見つけて下さる皆さんは当然御存知だと思うのですが,
ABCLとは"Armed Bear CommonLisp"というCommonLispの処理系の一つで,JVM上で動作するものです.

自分が知りうるJVMで動くCL処理系の中で一番ちゃんと動きます.

QuicklispやASDFがほぼ正常に動作し,多くのライブラリをABCL上でも使えるます.

なぜABCL?

最近,自分の研究で作るシステムの中で,JavaAPIしか提供していないアプリケーションをCommonLispで開発しているシステムに組み込めれば嬉しい,という状況になりました.

最悪,UIOPのrun-programなどを使って,引数をJavaコマンドに渡して叩く感じのマクロでも書こうかと思いましたが,速度も出なければプログラムもダサい気がする...

JVM上で動作するABCLなら非常にシンプルにJavaを操作できるようなので,とにかく試してみることにした,という経緯です.

あと最近(今年の6月)に最新版のABCL1.5.0がリリースされたというのも,この処理系に注目した理由の一つでしたね.

本稿の目的

細かい話とか,幾つもの例とかを出すのは今度別のエントリでやるとして,今回はとりあえず簡単な例が実行できるよね,ってところをお見せしたいと思います.

というのも,ABCLを使ってプログラムを書いている例が少なく,日本語の記事に至っては皆無と言えるほどでしたので,自分と同じくだらないことにハマってしまう人が現れないために書こうという趣旨です.

Clojureでいいじゃん」なんて思った方もいらっしゃるかとは思いますが,システムを書き換えるほどの時間的余裕はなかったので選びませんでした.1

では早速いきましょう.

ABCL(CommonLisp)からJavaのメソッドを呼び出す

今回はLispからJavaを呼び出す方法を書きます.

次のようなとてもシンプルなJavaプログラムを考えましょう.

public class SimpleCulc {
    public static int addTwoNumbers(int a, int b) {
    return a + b;
    }
}

SimpleCulc.javaという名前のファイルに,SimpleCulcクラスを定義し,そのクラスの一つのメソッドとして,addTwoNumbersというInt型の引数を2つ取りそれらの和を返す静的メソッドを定義しました.

今回はこのaddTwoNumbersメソッドをLispから呼び出してみます.

当たり前ですが,Lispから呼び出す前にコンパイルしてクラスファイルを生成します.

SimpleCulc.classというファイルがあることを確認できれば準備完了です.

呼び出しコードはこんな感じになります↓

;; Javaのクラスファイルのパスを通す
(add-to-classpath "/Path/to/SimpleCulc/")

(defun simple-culc (param arg1 arg2)
  (let* ((class (jclass "SimpleCulc")) ;; Javaのクラスを呼び出す
         (intclass (jclass "int")) ;; メソッドに渡す引数の型クラスを呼び出す
         (method (jmethod class "addTwoNumbers" intclass intclass)) ;; メソッドに狙いとするクラス名とメソッド名,引数の型クラスを渡す
         (result (jcall method param arg1 arg2))) ;; 定義したメソッドをコール(呼び出)し,結果を取得する
    (format t "~A~%" result)))

これだけです.簡単ですね! ちなみに恥ずかしいことに,僕はadd-to-classpathという関数を探すのに1日かかりました(^^;)

これ地味にハマりどころじゃないですか?僕だけですか,そうですか.

(試してないですが,多分普通にCLASSPATH環境変数が目的のクラスファイルのところに通っていれば動くはず)

関数を定義できればあとは実行するだけです!

CL-USER> (load "/Path/to/lisp-file.lisp")
T
CL-USER> (simple-culc t 2 4)
6
NIL

2+4が無事に成功して6が返ってきていることがわかります.

ただ,多少疑問に感じるかもしれないのはjcallの引数,特にtの部分ではないでしょうか.

Jcallの引数は,第1引数が呼び出す対象のメソッドオブジェクト,第2引数がそのメソッドのインスタンス,第3引数以降がメソッドの引数として扱われるようです.

今回の超簡単な例では静的メソッドを呼び出すだけなのでインスタンスを要求しませんね.

ただ仕様書には静的メソッドをコールするときにはNILを使えって書いてるんですけど,実際にNILを渡すとなぜかヌルポが返ってきます.

結果的に,nilでない何かしらのアトムを入れれば解決しました.

よくわかりません,もう一度仕様書を読んで出直します.
原因が分かる人がいらっしゃれば教えてください.

というかこの記事を書いたら早速製作者に問い合わせよう.(書き始める前にやれっていうね)

まぁ,何にせよ,最低限のコードは実行できました!

おわりに

JVM上で動くCommonLisp処理系の一つであるABCL(Armed Bear CommonLisp)についてざっくりとご説明させていただきました.

どうでしょう,使えそうだと感じたでしょうか,コレじゃあ使えないって感じでしょうか,それともまだ良くわからない感があるでしょうか.

また今度,勉強がてらちょっと詳しく書こうと思うので,詳細はその時に!

間に合えば第2回関西Lispユーザ会で発表させていただくかもしれませんね!

ご指摘,質問等お待ちしております!

参考

ドキュメントを探すのにも多少時間がかかったので,参考になれば...(2017年6月30日現在)


  1. 僕はあんまりClojureの記法が好きじゃないっていう理由もありますが…(笑)