Scala Conference in Japan 2013 に行ってきました

Scala Conference in Japan 2013に行ってきました。

技術的な話はスキル的にできないので、感想というか印象にのこったことを書きます。


これです。

まさに「10年くらいまえのJavaの熱気」でした。
自分語りみたいになりそうですが、かつて C++を書いていた頃の自分にとっての Javaと、いま Javaを書いている自分にとっての Scala。これらがとてもカブって感じるのです。書籍から引用してこれを代弁してもらいます。

1995 年にJava が現れたとき、Java は、ポインタやメモリ管理、その他の難解な配管工事のような仕事と戦い疲れたC++ プログラマを救済する言語として歓迎されました。Java は、そうしたプログラマの難題を解決してくれました。

しかし、2008 年現在、もはや下位互換性は意味がありません。

Java は、0 で始まる配列のようなC++ 主義や、プリミティブ型とオブジェクトの区別などの 1995 年には意味があったけれども最近の開発者の生産性に役立たないお荷物であふれかえっています。
ThoughtWorksアンソロジー P55

ちなみに 「かつての自分」は’95年じゃないですよ。大体10年くらいまえですね。若い人は最初からJavaだったりrubyだったりしますからオッサンホイホイ的エントリの予感です。

Scalaは Java VMで動作し、既存のJavaのライブラリを利用できますので「Javaな人」向けの Better Javaという位置にいます。カンファレンスで発表された竹添さんも、既存の資産を利用したいので Playは Servletでも動いてほしい、そういうニーズがあるということをおっしゃってましたね。

さて、Javaは次のような戦略をとりました。

しかし、Javaの成功のためには、当時の開発者にとって高位の聖職者ともいえるC++ 開発者にアピールする必要がありました。したがって、Java の設計者が意図的にJava 言語の見た目をC++ に似せたことには意味がありました。もう一度基礎からすべてを学び直さなければならないとしたら、開発者を新しい言語に引きつけることはできませんから。
ThoughtWorksアンソロジー P55

(当時 CっぽいC++を量産していた自分は聖職者ではありませんでしたが。)

それからJavaの父、James Gosling氏も言ってますね。

Javaと Javaの ASCIIコードレベルの構文は、Cプログラマや C++プログラマが簡単に使えるように設計されています。実際、とてもうまくいっています。ほとんどの Cプログラマや C++プログラマは Javaコードを少し眺めてこう考えます。「うん、これはわかる。」ってね。

「我こそが考えうる最高のプログラミング言語だ」と自負している理論的な世界の場合は、相互移行性は現実の目標にはなりません。

もしも私がJava を離れて自分専用のプログラミング言語を設計したとしたら、きっとほとんどの人は怒り心頭でしょうね
言語設計者たちが考えること P306

つまりJavaの言語仕様は、理想形を目指したわけではなく最初からマーケティング的な割り切りがあったわけです。やはり企業生まれですし。
自分も、C++ → Javaの時と同じように、Javaの延長線上に Scalaが居ることをある程度期待しているのかな、と、気づきというかあらためて認識しました。
一方、Scalaは Better Javaだけでなく、関数型プログラミングという Javaにはないパラダイムも同時に実現しようとしています。なので、確かにJavaっぽく書けますが、Javaっぽくない(関数型っぽい)書き方もできます。その他にも強力な機能があって(理解していない機能も まだまだいっぱいあるし)、そういう Scalaコードだと自分にはまだ読めません。わかってしまえば簡単なのかもしれませんが、自分にとってはまだ謎コードも多いです。もちろん読めるようになりたいんですけど、演算子とか記号はググりにくいんですよね。

ところで先日、周りのJavaな人たちに自分のこのScala熱を浴びせてみたのですが、better Java以外のところについては、あまり反応がよくありませんでした。それで、Better Javaだけならば Groovyを使うという選択もありえますし、Java 8で型推論とラムダ式がサポートされたら Scalaに近いBetterが達成できるじゃないか、と。いや、自分がうまくそれ以外のところを説明できてないことによるものですが。

ということで、Scalaを推すときは、Better Javaだけでなく、関数型プログラミングとか、(カンファレンスで望月さんが発表された)Betterオブジェクト指向プログラミングとか、Javaよりもっと強力だってことをうまく説明できるようになりたいですね。スキル尚早だったかも。初心者の自分でも思いつくのは、trait, case class, Option, 代数的データ型、パターンマッチ、Javaより強力なジェネリクスやcollection framework。それから、関数型プログラミングの考え方、immutable, 抽象化, データ構造, 再起(書き方というより、再帰的な性質を見つける考え方とか)全然網羅的ではなく思いつくまま書いてますが、こまかくたくさんありますよね。
それから伝わらないかもしれませんが、プログラミング=処理を書くもの。という考え方から、プログラミング=問題解決。という考え方になってきた気がします。(少し大げさに言ってます:-p)
挙げればいくつもあるんですが、その素晴らしさをうまく言語化できると良いのですが…
なので、もっとまともに説明できるように勉強しないと、という話になって残念エントリになってしまいますね。。。
まぁ懐疑的な人も食わず嫌いせずに少し何かScalaで書いてみたら、日本語で説明されなくても、良さが分かるってこともあると思いますよ。

最後に

Scalaの普及には、それ自体の良さだけでなくてマーケティングはある程度必要なのかなぁと勝手に思ってます。
でも Scalaが必要以上に Javaユーザに媚びるようなこともして欲しくないし、ましてや 一時期の Agileのような、例えば「WFは悪!あいつらはいつも分厚い紙束のドキュメントを持ち寄って何時間も会議してる」みたいな煽りで「ああAgileはドキュメント書かなくていいのかー」みたいな誤解でもって広まったりもしてほしくないです。そういうのは、てっとり早く広まるかもしれないけど Agileの人たちはそんな誤解を解くコストを後から結構払ったわけで(でもだいぶそういう極端な誤解はなくなりましたけどね。煽ってた人とは別の人たちがずいぶんがんばったのではないでしょうか。)
もちろん、Scalaが普及してほしいんですけども。簡単にあーすればよいとか言えないですね。

Scalaのマーケティングとかどうこう言える立場でもないですが、そんなことを思ったのでした。

広告

[ORM][Java] JavaのORMをいくつか比較してみる

昼間のお仕事の関係でORMをいくつか触ったので、比較してみることにした。今更なーという感じはするけど、現時点で選ぶなら何なのだろうかと考える人の一助になれば。

※ 2012/09/18 改訂。Spring DATA JPAと QueryDslについては詳細をGistにあげたので、リンクを貼りました。

  • Java Persistence API (JPA)
    • JSR-317. 一応JavaEE6の標準になっているが、使いやすいわけではなく補助的に組み合わせて使うライブラリがいくつか存在するのでそれらについて後述。
    • 大まかに、クエリを書く方法は2通り
      • JPQL(SQLに似非の独自クエリ言語)を文字列で書く
        • 例: select o from Order o where o.date < '2012-09-01'
      • Criteria API(メタクラスを生成すればある程度は型安全だが、APIが使いにくい)で書く
        • クエリ言語として流れるように書けないため、表現力が低い
        • これを解決するのが後述するQueryDsl。Blog記事が参考になる。
  • Spring DATA JPA
    • Spring Frameworkの拡張ライブラリ(springframework-jdbcシリーズのようだが、安定したら本流に組み込まれるのかもしれない)
    • Gistに詳しく書いてみました。こちら
  • QueryDsl(JPA)
    • Open Sourceのライブラリ。クエリを型安全で流れるようなインタフェースなDSLで記述することができるライブラリで、JPAのCriteria APIに変換するDSLと、直接SQLに変換するDSLとがある。後者はQueryDsl(SQL)として後述。
    • 前者のJPAのCriteria APIに変換するDSLについては、QueryDslの Blog記事を参照のこと。コードを見てもらえば、これ以上特筆することはない。
    • ちなみに更新系のinsert, update, delete処理は、JPAのEntityManagerのAPIをそのまま使用する
  • QueryDsl(SQL)
    • QueryDSL(SQL)については Gistに詳しく書いてみました。こちら
  • Spring DATA JDBC Extension
    • Spring JDBCの拡張ライブラリ。上述の Spring DATA JPAともまた別モノ。
    • Spring JDBC本体の JdbcTemplate から QueryDslの型安全クエリを生成できるが、流れるように書けないという残念なAPI。
    • 上記QueryDsl(SQL)の説明Gistに一緒に書きました。

 

ここに挙げたライブラリの動作確認コードは、 githubに上げてます。

総合評価的なマトリクスは昼間のお仕事でそのような流れになったら作成する予定。いま選ぶとしたら個人的には QueryDsl(JPA)かなぁ。

それから、実行時性能を比較するためにベンチマークのテストを仕込んでいるところ。これについては後日書く予定。

[gradle] Java6 Annotation Processing

  • QueryDsl(JPA)を例に GradleでAnnotation Processingを実行させてみる
  • antの例があるので、ほぼこのままだったりするけど。。

annotation processingとは

dependenciesの設定

  • Annotation Processorを含むjarに対する依存関係を設定する。QueryDslは本体機能へのAPI(querydsl-jpa.jar など)とは別に、Annotation処理のjarが切り出されている(querydsl-apt.jar)。querydsl-apt.jarは、実行時には不要であるため、独自にdependency condfigurationを設定した。(下記processor)

 configurations {
      processor {
          description: "classpath for annotation processors"
      }
  }
  • dependenciesクロージャで、processorquerydsl-apt.jar を指定する
 dependencies {
      compile "org.eclipse.persistence:javax.persistence:2.0.0"
      compile "org.eclipse.persistence:eclipselink:2.4.0"
      compile "com.mysema.querydsl:querydsl-jpa:2.7.2"
      compile "org.slf4j:slf4j-log4j12:1.6.6"

      processor "com.mysema.querydsl:querydsl-apt:2.7.2"
  }

antのjavac呼出タスク定義

  • Annotation Processingの出力先は、build/processorGen/srcとした。今回は拡張プロパティext.destdirに設定しておく。

task execProcessor {
      ext.destdir = new File(buildDir, "processorGen/src")
      // ...
  }
  • buildディレクトリ配下としたのは、cleanタスクで消してくれるため。例えばモデルクラスの名前を変更すると旧名のメタクラスが残ったままになってしまうなど、cleanできたほうが都合がよい。

  • もちろん毎回消して再生成もよいのだけど、今回はUP-TO-DATEを仕掛けてビルドを効率化したかった。
  • UP-TO-DATEは下記のように inputsとoutputsにそれぞれファイルまたはディレクトリを設定することで、変更の有無(つまりAnnotation Processingタスクの実行が必要かどうか)をGradleが判定してくれるようになる。

 task execProcessor {
      ext.destdir = new File(buildDir, "processorGen/src")
      inputs.source(sourceSets.main.java)
      outputs.dir(ext.destdir)
      doLast{ /* ここにjavacを呼び出す処理 */ }
  }
  • doLastクロージャで、このタスクの実行処理を記述する。この例ではantjavacを呼び出してみる。

doLast {
      ext.destdir.mkdirs() // 出力先が事前に存在しないとエラーになるのを回避

      def primaryOptions = [
          includeAntRuntime: false,
          destdir: ext.destdir,
          classpath: configurations.compile.plus(configurations.processor).asPath,
      ]

      ant.javac(primaryOptions) {
         sourceSets.main.java.addToAntBuilder((Object)ant, 'src', FileCollection.AntType.MatchingTask)
         compilerarg(value: "-proc:only") 
         compilerarg(value: "-implicit:none")
         compilerarg(value: "-processor")
         compilerarg(value: "com.mysema.query.apt.jpa.JPAAnnotationProcessor")
      }
  }
  • 上記のant.javacはおおよそ下記のようなant記述と同じ意味

    <javac includeAntRuntime=false
           destdir="${ext.destdir}"
           classpathref="${compiler.plus(processor).asPath}">
      <src ...ここはaddToAntBuilder()がゴニョゴニョする  />
      <compilerarg value="-Xlint"/>
      <compilerarg value="-proc:only"/>
      <compilerarg value="-implicit:none/>
      <compilerarg value="-processor/>
      <compilerarg value="com.mysema.query.apt.jpa.JPAAnnotationProcessor/>
    </javac>

compileJavaの設定

  • これで execProcessorを実行すればQueryDslによってメタクラスがbuild/processorGen/srcに生成される。だがこのソースが compileの対象になっていないのでこのままではコンパイルされない。
  • ここで src/main/javaディレクトリにコピーするという手もあるが、UP-TO-DATE判定を考えると好ましくない。また、Checkstyleといった静的解析ツールが処理する対象ディレクトリから自動生成されたコードを分離したいということもあるかもしれない
  • ということで、compileJavaの対象ソースディレクトリとして execProcessorの出力先ディレクトリを追加する。
  • また、compileJavaの依存タスクとして execProcessorを設定する。

compileJava {
      dependsOn execProcessor
      source execProcessor.ext.destdir
 }

これでcompileJavaを実行するとexecProcessorが先に実行される。

$ gradle compileJava
:execProcessor
[ant:javac] 注意:Running JPAAnnotationProcessor
[ant:javac] 注意:Serializing Entity types
[ant:javac] 注意:Generating .... for [...]
[ant:javac] 注意:Running JPAAnnotationProcessor
[ant:javac] 注意:Running JPAAnnotationProcessor
:compileJava

BUILD SUCCESSFUL

また、変更がなければ UP-TO-DATEと判定され、実行されない。

$ gradle compileJava
:execProcessor UP-TO-DATE
:compileJava UP-TO-DATE

BUILD SUCCESSFUL

[gradle] Eclipse Gradle STS Support と gradle-gae-plugin

はじめに

Gradleが気になっていたので使ってみたメモ。
Eclipseと統合されているのがよいので、Eclipseの拡張機能(Gradle STS Support)を試してみた。
ついでにGoogle App Engineのアプリケーションをgradleからデプロイする方法も試してみた
今回試したのは下記のバージョン。

セットアップ

Gradleのサイトによると、SpringSourceのSTS(Spring Tools Suite)にGradleサポート機能があり、Gradle機能だけを個別にインストールできるとのこと。Springを使うわけではないのに(いや、使うにしても)重量級のSTSを入れなくてもすむのは助かる。

Gradle STS Supportのインストール方法はGradle STS Support — 2.9.0 — Installation(英語)の通り。一緒に gradle-1.0-mileston-7 もプラグインとしてインストールされるので、gradle自体のインストールは不要(※単なるJavaプロジェクトならこれでも十分だと思うが、実際は gradle-gae-pluginが動かなくて結局別のバージョンを使った。詳しくは後述する)

まずはeclipseでプロジェクト構成を作る。デフォルトのディレクトリ構造にしておくと(もちろん変更できるが)面倒が少ない。webアプリケーションのファイル(jspなど)はwarプラグインのデフォルトディレクトリ構造にしておく。

で、build.gradleを書いて、プロジェクトのディレクトリ直下に置く。GAEとslim3を使う場合はこのような例に。

build.gradle
apply plugin: 'java'

def gaeVersion = '1.6.4'
def slim3Version = '1.0.15'

repositories {
    mavenCentral()
    mavenRepo(url: 'http://maven.seasar.org/maven2/')
}

dependencies {
    compile 'javax.servlet:servlet-api:2.5'
    compile 'javax.servlet:jsp-api:2.0'
    compile "com.google.appengine:appengine-api-1.0-sdk:$gaeVersion"
    compile "com.google.appengine:appengine-api-labs:$gaeVersion"
    compile ("org.slim3:slim3:$slim3Version") {
        exclude group: 'com.google.appengine', module: 'appengine-api-1.0-sdk'
    }
}

Gradle STS Support — 2.9.0 — Tutorial(英語)に使い方は書いてあるが、下記に簡単に書く

  • (チュートリアルではImportを使っているが、)プロジェクトの右クリックメニュー→[Configure]→[Convert to Gradle Project]をすると、Gradleプロジェクトとして認識される。チュートリアルに出てくるような「G」マークがプロジェクトに付くようになる
  • build.gradleを右クリックメニュー→[Gradle]→[Enable Dependency Management]を選ぶと、ビルドパスに[Gradle Dependencies(persisted)]というLibraryが追加され、build.gradleに書いた依存関係にあるjar(dependencies {…}に書いたslim3-1.0.15.jarなど)が追加される。
    • なお、build.gradleのdependenciesの記述を更新しても自動的には反映されない。手動で[Gradle]->[Refresh Dependencies]を実行する必要がある
  • Gradle Tasks Viewを開き、[Project]を選択すると、タスク一覧が表示される。たとえば compileJava をダブルクリックすればGradleによってコンパイルが動作する
  • もちろん、Eclipseのビルドパスにjarが追加されているのでEclipseでのコンパイルもできる

Google Eclipse Pluginでのデプロイには、warディレクトリを構成しなくてはいけないので、そういうCopyなどのタスクを定義するというやり方もあるだろう。
しかしもう少しスマートに gradleから Google App Engineに Deployする方法を書く。この方法だとGoogle Eclipse Pluginは特に不要である。

gradle-gae-plugin

gradleは汎用的に設計されたビルドツールであり、例えば javaをビルドしたいときは apply plugin: 'java' と記述して、 java プラグインを適用する。javaやscalaなどの標準で組み込まれているプラグインが多数あるが Google App Engineのプラグインは標準にはない。今回使用するgradle-gae-pluginはbmuschko氏のサードパーティ製プラグインである。
使い方は簡単で、readmeにあるとおり下記をそれぞれ追加するだけである。(7/7 0.7.3時点のreadmeに修正。今はグループIDがorg.gradle…になり、maven central repositoryから取得可能となった)

pluginを指定する

apply plugin: 'gae'

buildscriptを記述する

buildscript {
    repositories {
        mavenCentral()
    }

    dependencies {
        classpath 'org.gradle.api.plugins:gradle-gae-plugin:0.7.3'
    }
}
ちなみに:

buildscriptdependenciesには、このgradleビルドスクリプトを動作させるために必要なライブラリへの依存関係を解決するためのもので、サードパーティのプラグインをここで定義する。先に述べたdependenciesのcompileなどに定義しても動くと思うが、compileに定義したjarは war/WEB-INF/libの下に含まれてしまう。gradle pluginや、例えば slim3-gen.jarのようなビルドスクリプト動作時にのみ必要なライブラリはここに書くのがよい。

さてここで EclipseのGradle Tasks Viewを更新する。(矢印が回転しているアイコンのボタンをクリック。ツールチップに「Refresh task list」とでる)
すると、下記のエラーになって止まってしまう。

org.gradle.internal.service.ServiceRegistry
Could not fetch model of type 'EclipseProject' using Gradle distribution 'http://repo.gradle.org/gradle/distributions/gradle-1.0-milestone-7-bin.zip'.

ここでEclipseのメニューバー[Window]→[Preferences]→[Gradle]のGradle DistributionのURIに

と入力したところ、うまく動作した。

現象的にはGRADLE-2185の問題に近いと思われる。
実際、いろいろ調べている間、milestone-9で上記同様の指定をしたのだがうまく動かなかった。そのうちrc-1がリリースされて試してみたらうまくいった。

Gradle Tasks Viewに gaeXxxというタスクが並んで見えるようになるはず。
デプロイの場合は、その中のgaeUploadというタスクを実行し、appspotのログインID(emailアドレス)やパスワードを入れるとデプロイができる。他のタスクについては、readmeを参照のこと。

gaeDownloadSdkについて

Google App EngineのSDKをどこかに展開している人は、環境変数のAPPENGINE_HOME または システムプロパティの google.appengine.sdk にパスをしていしておけばよいが、gradle-gae-pluginにはSDKをダウンロードしてくるタスクもある。これでどの環境でも同じSDKのバージョンを使用することになるし、SDKのバージョンアップもGradleのビルドスクリプトだけで完結する。

依存関係として、gaeSdk configurationに、GAEのSDKを指定する。

dependencies {

    //compile .......
    //  ..... 

    gaeSdk "com.google.appengine:appengine-java-sdk:$gaeVersion"
}

gae.downloadSdk = trueを指定する

gae {
    downloadSdk = true
}

それから、gradle-gae-pluginのバグなのか、二回目以降のビルドで gaeDownloadSdkがUP-TO-DATEの時にSDKパスがAPPENGINE_HOME にも google.appengine.sdkにも指定されていないというエラーとなってしまうので、自分はこのように設定している
(7/7訂正 0.7.3で確認したところsystem propertiesの設定は不要になっていた。バグだったのがFIXされた模様)

gae {
    downloadSdk = true
    System.properties['appengine.sdk.root'] = gaeDownloadSdk.explodedSdkDirectory.getPath() + File.separator + "appengine-java-sdk-$gaeVersion"
}

4/18追記
gaeUploadの途中でemailとpasswordを聞かれ、Eclipseのコンソールに入力すると動作していたのだが、最近うまく動かなくなったので回避策を。

build.gradleのgae{}にappcfg{}を定義して、emailとpassIn=trueを設定。
※ここにpassword=xxxxxxxと入れてもよいが、build.gradleはリポジトリに入れてるので外だしにする。

gae {
    appcfg {
        email = 'your_email_address@gmail.com'
        passIn = true
    }

    downloadSdk = true
    System.properties['appengine.sdk.root'] = gaeDownloadSdk.explodedSdkDirectory.getPath() + File.separator + "appengine-java-sdk-$gaeVersion"
}

~/.gradleディレクトリ(Windows 7の場合は C:\Users\ryo-murai\.gradleなど)にgradle.propertiesファイルを置いて、下記のように書く。

gradle.properties

gaePassword=your_google_password

これで、指定したemail/passwordが使用される模様。
追記おわり

まとめ

  • EclipseとGradleの統合に、EclipseプラグインであるGradle STS Supportを使ってみた
  • Google App Engineへのデプロイにgradle-gae-pluginを使ってみた
    • Gradle STS Support同梱の gradle-1.0-milestone-7では動かないので、1.0-rc-1を指定する
  • gaeDownloadSdk使ってもSDKへのパス設定が必要だった。(オレだけ?)

補足

この記事のリンクは
gradle-1.0-rc-1のマニュアルやGradle STS Support 2.9.0のドキュメントを参照しているが、GradleはURLの /docs/1.0-rc-1/userguide//docs /current/userguide/に、またSTSは/sts/docs/2.9.0/reference//sts/docs/latest/reference/に変更すると、最新のバージョンのドキュメントへのURLとなる。