「Java 開発者のための関数プログラミング」を読んで

Java 開発者のための関数プログラミング という本をとんぷーが翻訳した (「Java開発者ための関数プログラミング」が出版されました) というので ePub で買って読んでみた. 前からとんぷーが何か本を翻訳している話は聞いていたし, タイトルにすごく惹かれたので, 出てすぐに買い iPod touch に放り込んで主に寝ながら読んでいた.

(特にアプリは入れずに iBooks で読んでたけど, 本文に対してメモが取れるところが便利.)

内容について

おおよそタイトルから想像した通りの内容だったが, Java の動くソースコードをちゃんと書いて説明しているところにこの本の価値を感じる. 余談だが, bleis さんの Java の語彙で Maybe を説明してみる という記事も近いものに思う.

通常の Java クラスをモジュールとして使ったり, 継承を使った多態を否定して使わなかったり, Java プログラムとしては挑戦的な作りになっている. こういう「無茶しやがって」感が面白い. いわゆる関数型プログラミング言語に出てくる仕組みを別の仕組みを持つ Java で実装してみるというのは, その仕組みを複数の視点から見てより理解を深めることになる. Java で無理して関数プログラミングをすることで, 関数プログラミングのスタイルがどんなもので, どれだけ Java がそのスタイルに向いてないかが良く分かる.

内容が「関数プログラミングで書くと何が良いか?」という一貫した視点で書かれていて読み易い. 色んな論点を網羅的に扱っているところも有難い. もちろん翻訳された日本語に不自然さがないところも素晴しい. 通しで読めば1日で読めてしまうのではないだろうか?

(内容も文体もどことなくブログ記事っぽく感じるのは何故なのだろう)

クスッとしたとこ

意図的かは分からないが, 原著のタイトルの "functional programming" を「関数型プログラミング」でなく「関数プログラミング」と訳したのはけっこう好き. たぶんとんぷーのことなので意図的なんだろう.

「Customerクラスは顧客に関するすべての情報を突っ込む福袋のようになっていました」というところ.「福袋」という表現がぴったりハマっていて小粋なところがにくい.

「Nullとはなんだったのか」という節のタイトル. よくブログ記事とかで見るが, null の面倒さを知ってるとウンウンと頷くタイトルだ.

本筋とは全く関係無いが, クラス NaturalNumbers に定数 (public static final なフィールド) ZERO があるということは, 著者は「0 は自然数」の人なんですね. そうなんですね.

誤植

「本書で使用されている表記規則」

半角の閉じ括弧 (")") が余計.

固定幅文字(Constant width))
                          ^

System Message: WARNING/2 (<string>, line 43)

Literal block ends without a blank line; unexpected unindent.

「コンビネータ関数:コレクションの強力なツール」

組版ツールの関係だろうが, 何やら不思議な文字列が残っている. たぶん "@" というのが特殊記号なのだろう.

filterは「head」と@「<tt>{filter}をtailに適用した結果」
                  ^  ^^^^^^^^^^^^

(指摘されるとけっこう凹むのだが, 気付いてしまったので一応報告)

おわりに

Java で関数プログラミングをしてみる, という1つの発想から書かれた本ですごく読み易い. 関数プログラミングの仕組みについて頭を整理するのにも良い.

既に関数プログラミングを理解している人は読む必要はあまり無いが, 既存の解説読んでもピンと来なかった人が読むと良いかもしれない. 人によって理解が進む解説は様々なので, 色々なスタイルの解説があることはとてもよいことだ.

こういった文章を書いて, プログラミングをする人達に貢献していくことも素敵なことだと思う.

とにかく読んでて楽しい本でした.

JUnit4 の Parametrized を Groovy で使う

概略

現在こっそり開発しているツールのテストに Groovy を使用しています. テスト対象は Java なのですが, テストには柔軟に書ける Groovy が便利です.

プロジェクト自体は Maven3 で管理していて, ビルド, テスト, パッケージングを全て pom.xml で設定しています. そこのちょっとした設定の間違いで数時間ハマってしまったので, 記録として残しておきます.

Groovy テストスクリプト

JUnit4 ではパラメータ化されたテストを実行することができます.

クラスに org.junit.runner.RunWith アノテーション, パラメータを供給するメソッドに org.junit.runners.Parameterized.Parameters アノテーションを付けると, そのクラスはパラメータ化されたテストを実行できるようになります.

詳しいことは http://groovy.codehaus.org/Using+JUnit+4+with+Groovy あたりを見てください.

pom.xml

pom をそのまま書くのが面倒なので,

<aaa>
  <bbb>
    <ccc>value</ccc>
  </bbb>
</aaa>

というのを

aaa
`-bbb
    `-ccc=value

と書くことにします.

プロジェクトの build 要素以下はこのようになっています.

build
`-plugins
  `-plugin
    `-groupId=org.codehaus.gmaven
    `-artifactId=gmaven-plugin
    `-version=1.4
    `-configuration
    | `-providerSelection=1.8
    `-executions
    | `-execution
    |   `-goals
    |     `-goal=generateStubs
    |     `-goal=compile
    |     `-goal=generateTestStubs
    |     `-goal=testCompile
    `-dependencies
      `-dependency
        `-groupId=org.codehaus.groovy
        `-artifactId=groovy-all
        `-version=1.8

この pom で mvn clean test を実行すると以下のようなエラーが出ました.

シンボルを見つけられません。
シンボル: クラス Parameterized$Parameters
場所    : org.junit.runners の パッケージ

Groovy で書いたテストスクリプトが Java クラスに変換され, さらに javac でコンパイルされます. その段階で上のエラーが出ています.

確かに Groovy から変換された Java ソースを見ると @Parameter アノテーションが @org.junit.runners.Parametrized$Parameter に変換されてしまっています. Java ソースとしては $ ではなく . が正しいはずなのでコンパイルエラーが出た理由が分かりました.

しかし, いくらググってもこの Groovy スクリプトのマズいところが見付かりません.

ふと思い立って goal=generateTestStubs の部分を消してみました. 「変な Java ソースを生成するくらいなら, そこを消してしまえ」と, 思い立ったというよりヤケクソでというのが正しい表現な気もします. すると無事テストが実行されました.

なぜかと考えてみると, そもそも generateTestStubs で生成された Java ソースは, メソッドの本体が return null のような意味の無いクラスでまさにスタブでした. なぜこのようなクラスが必要になるのかと言うと, おそらく Java 側からこの Groovy スクリプトを呼び出すコードをコンパイルするためのクラスなのでしょう. 今回, そのような Java クラスは無いので実はこのゴールは不要なのでした.

まとめ

ということで答えは「余計なゴールがあったために, 変な Java ソースをコンパイルしなくてはならなくなり, そこでエラーが起きていた」ということでした.

う〜ん, 我ながら嫌なハマり方をしましたが, 解決できたときはスッキリしました.

そして教訓は「ツールについてきちんと理解しよう」ということでした. maven は便利なツールですが, まだ自分にとって複雑なツールです. まだまだ勉強しないとですね.

それでは.

playing with maven (1)

最近, 仕事で maven を使うことが多くなり, 自分でも色々触ってみたかったのでいじってみました.

(まだ雛形しか作ってませんが)

環境

maven のバージョンなどは以下の感じです. (まだ Lion にしてなかったや ^^;)

$ mvn --version
Apache Maven 3.0.3 (r1075438; 2011-03-01 02:31:09+0900)
Maven home: /usr/share/maven
Java version: 1.6.0_26, vendor: Apple Inc.
Java home: /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
Default locale: en_US, platform encoding: MacRoman
OS name: "mac os x", version: "10.6.8", arch: "x86_64", family: "mac"

maven plugin

せっかくなので maven plugin の作り方も勉強します.

http://www.slideshare.net/guestd4898b/maven2

ここに素晴しいスライド資料を見付けたので, これに沿って plugin プロジェクトを作成. maven のバージョンが 2 と 3 で違っていますが, そんなに大きな違いは無いようです. (maven の 1 と 2 では大違い, という話は伝え聞いていました.)

$ mvn archetype:create -DpackageName=net.elliptium.maven.plugins.sample -DgroupId=net.elliptium.maven.plugins -DartifactId=maven-sample-plugin -DarchetypeArtifactId=maven-archetype-mojo
$ cd maven-sample-plugin
$ mvn install

でプロジェクトを作成し, ローカルレポジトリに install します.

最初のプロジェクト作成のコマンドは少し長いですが, スライドの17枚目を見ていただくと理解できるかと思います.

packageName は Java の慣習に従って, このブログでも使っている私有のドメイン名を引っ繰り返したものの下にくるものを付けました. groupId も他人のものと区別できるように, 似た感じに付けました.

maven の素晴しいところは最初に雛形となるプロジェクトが作成され, 最低限の内容が記述されたソースコードと設定ファイルが出来上がるところです. Rails から始まった流儀なのかなぁ? となんとなく思っていますが, その思想は現在は Sphinx や PlayFramework など, 言語や分野の壁を越えて広まっています.

最初の一歩を踏み出す人には, 実際に動くサンプルソースから始めて, 少しずつそれを改造しながら進むのが一番と考えています. なので, この maven の思想には強く共感を覚えます.

さて, プロジェクトを作ったら, プロジェクトのディレクトリに入り最初のコンパイルをしましょう.

maven には build lifecycle という概念があって, 開発→コンパイル→テスト→出荷のような一連の開発の流れを管理しています. それぞれの1ステップのことを phase と呼ぶそうです.

http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#Build_Lifecycle_Basics

今回は install までやってしまうので, .class ファイルへのコンパイルをし, jar に固め, ローカルレポジトリに入れるところまで行います.

$ ls target/
maven-sample-plugin-1.0-SNAPSHOT.jar
$ ls ~/.m2/repository/net/elliptium/maven/plugins/maven-sample-plugin/1.0-SNAPSHOT/
maven-sample-plugin-1.0-SNAPSHOT.jar

plugin プロジェクトのディレクトリに居るものとして, 成果物の出力先である target ディレクトリの中を見ると .jar ファイルが出来上がっています. また maven のローカルリポジトリにも同じ .jar がコピーされています.

package するだけでなく install まで行なうと, 他のプロジェクトからもこの .jar ファイルが使えるようになり, plugin を使用することができます.

maven project

さて, 次はこの maven plugin を使う Java プロジェクトを作成しましょう. ここでも maven を使うことができます.

$ mvn archetype:generate -DgroupId=net.elliptium.java.sample -DartifactId=sample
$ cd sample
$ emacs pom.xml
// なんらかの方法で pom.xml を編集
$ cat pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http ...

Licenses