読者です 読者をやめる 読者になる 読者になる

夢とガラクタの集積場

落ちこぼれ三流エンジニアである管理人の夢想=『夢』と、潰えた夢=『ガラクタ』の集積場です。

Clojure勉強日記(その11 3.3 ClojureでJavaのクラスを作る(Vol_2)

こんにちは。とりあえず、コンパイルができなかった問題をさらに追ってみます。

この辺のページを確認したところ、原因は「Classファイルの出力先ディレクトリが存在しないから」でした。
・・・というか、である調でブログを書くこと自体が結構厳しいことに気付きましたので、
とりあえずいつもの文体に戻します。

「CompilerException java.io.IOException: 指定されたパスが見つかりません。, compiling:(reader/TaskList.clj:1:1)」
からは想像がつかないエラーでしたが、とりあえず解消しました。

尚、Classファイルの出力先は下記のコマンドで確認可能です。

user=> (str *compile-path*)
"classes"

そのため、起動バッチも下記のように修正します。
classesディレクトリをわざわざ作っているのはJava系プロジェクトである関係上.gitignoreで
classesディレクトリを管理対象から外しているためです。

@echo off

set CLOJURE_HOME=%~dp0
set LIB_PATH=%CLOJURE_HOME%\lib
set SRC_PATH=%CLOJURE_HOME%\src
set CLASS_PATH=%CLOJURE_HOME%\classes

mkdir %CLASS_PATH%

java ^
-cp ^
%LIB_PATH%\clojure-1.5.1.jar;^
%LIB_PATH%\clojure-contrib-1.2.0.jar;^
%LIB_PATH%\criterium-0.4.0.jar;^
%SRC_PATH%;%CLASS_PATH% ^
clojure.main

pause

ディレクトリ構成は下記のようになっています。

ROOT
│  .gitignore
│  README.md
│  startRepl.bat
│
├─classes
│
├─lib
│      clojure-1.5.1.jar
│      clojure-contrib-1.2.0.jar
│      criterium-0.4.0.jar
│
└─src
    │  functions.clj
    │
    └─reader
            TaskList.clj

ともあれ、無事コンパイルはできるようになりました。

user=> (str *compile-path*)
"classes"

コンパイルするとclassesディレクトリ配下に下記のようにファイルが作成されます。
コンパイルがうまくいった証拠・・・ということになりますね。

├─classes
│  └─reader
│          TaskList$fn__4.class
│          TaskList$loading__4910__auto__.class
│          TaskList$_main.class
│          TaskList.class
│          TaskList__init.class

で、実際にJavaコマンドから実行してみると下記のようになりました。
Java側からClojureのコードで記述したソースを扱えているということになりますね。

C:\Develop\Source\GitHub\clojurestudy>java -cp lib\clojure-1.5.1.jar;lib\clojure-contrib-1.2.0.jar;lib\criterium-0.4.0.jar;classes reader.TaskList TestArg TestArg2
Received Arg is TestArg
Received Arg is TestArg2

では、この後は実際に関数を記述して確認します。
main文を下記のように改造し、task-list関数を呼び出して結果を出力するようにします。
・・・Javaから呼ばれることを前提とすると、関数と扱うべきなのかメソッドと扱うべきなのか微妙ですが(汗

(defn -main [& args]
    (doseq [arg args]
        (println (task-list arg))))

その上で、「task-list関数」を「main文の上」に定義します。
main文でtask-listを使用しているため、事前に定義されている必要があるようです。
・・・と本にありましたが、実際にそうか試してみます。

(defn task-list [filepath]
  (let [handler (new reader.TaskList)]
    (.parse (.. SAXParserFactory newInstance new SAXParser)
           (new InputSource (reader (new File filepath))) handler)
  @(.state handler)))

main文の下にtask-list関数を配置してコンパイル

user=> (compile 'reader.TaskList)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: task-list in this context, compiling:(reader/TaskList.clj:15:18)

main文の上にtask-list関数を配置してコンパイル

user=> (compile 'reader.TaskList)
reader.TaskList

と、本当に関数が上に配置されていないとエラーになりますね。
いえ、Clojure REPLがインタプリタであることを考えれば当然の結果なんですが、
ただ、ということはClojureソースコードに「メソッドを利用される順」に記述する必要があるということですか・・・

これはJavaをはじめとしたオブジェクト指向の考えでソース組んでいると中々苦戦しそうですね。
Javaの場合外部に公開しているインタフェースとしてのメソッドを大抵ソースの上に記述しますので。
まぁ、Clojureの場合は外部に公開しているインタフェースから利用されるメソッドは予め別Clojureソースに
記述しておけばいいという考えもありますが。

または、REPLでない状態でClojureソースをコンパイルする際には
上に書いても下に書いても問題なくコンパイルされるのかもしれない。
・・・どっちなんでしょうね?
とりあえず、やってみればわかる気もしますが現状REPLからしかコンパイルできないというw

この辺りって、JavaClojureを両方使って開発をしている方々の中ではどんな結論付けになっているんでしょうね・・・?