JJUG CCC 2013 Springに参加してきました #jjug #jjug_ccc

今日は朝早くから、西新宿でJJUG CCC 2013 Springに参加してきました。(まとめ)
久々の1日の勉強会で楽しかったです。あと都庁を間近かで見れてよかった。
ブログ書くまでが勉強会、という基本を新ためて学んだので、書きます。

基調講演-1 Javaのこれからを考える #ccc_k1

変容することが確実なこれからのシステム開発、それに耐え得る「サステイナブルSI」を考える。
途中から入りました(すみません)ので半端なメモです。

  • 資料: http://www.slideshare.net/yusuke/java-jjug-ccc-2013-spring
  • サービス全体がJavaだけで完結することはないが、 Javaはプラットフォーム、Javaはオープン、Javaはエコシステム、である、という特長は活きる。
  • Javaの未来もその変化に追随変化していく
    • Java9(Cloud, Multitenancy, GPU, Jigsaw.)
    • Oracleはコミュニティと上手くやっているのだが、驚きとイノベーションは少ないなあ。
    • JVM上の言語は進化しているし。

基調講演-2 What’s New for JavaFX in JDK 8 #ccc_k2

JavaFX、ちゃんと生きてます!、という話。

  • 資料: http://www.slideshare.net/OracleMiddleJP/jjugwhatsnewforjavafxinjdk8
  • JavaFXイベントハンドラがJava8 Lambda式でわかりやすくなる
  • 「モデナ」というUIテーマが追加。
  • 3Dグラフィック機能追加(ジオメトリ、カメラ、ライト)
  • 質問: iPadJavaFXを去年デモしてたけどあれはどうなった?
    • あれはコンセプトプルーフプロトタイプであって、提供予定はない(キリっ。OSSなので、誰でも可能性がありますよ。いつ参加する?今でしょ!!

Java EE 6 から Java EE 7 に向かって #ccc_h1

Java EE 7の情報。押えておくべき。

  • 資料: http://www.slideshare.net/OracleMiddleJP/java-ee-6-to-java-ee-7
  • プロファイルによるサブセット化はJavaSEの方にも将来的に入るよ。ラズベリーパイ用のJavaはそれを使ってるはず。
  • Java EEAPIは枝刈り(プルーニング)をして、古い・使わないものは整理します。
  • JAXRPC,JAXR,EJB Entity Beanは、Jax-WS, JPAになります。
  • 古いのはそのうち削除しますので移行してください。
  • JavaEEのコンテナ(APサーバ)をJavaSEから立ち上げられるようになります。
    • 各社のAPサーバ製品を共通のJavaEEコンテナと見做してインスタンス化・起動するための共通APIができます。
  • APIについては
    • Concurrency,Batch,JSON,WebSocket→新規
    • JSF2.2,JAX-RS→大幅な変更
  • JSON JSR 353。前とは結構変化した。
    • Streaming API..低レベル(StAXみたいな)
    • Object Mode API…かんたん。Streamig API上に構築(DOMみたいな)
  • EL3.0ではメモリ上のオブジェクト群に対してLINQっぽい操作ができる(例を見る限り、Lambda式が使える、ということに思えたけど…)。

Project Lambda Essential #ccc_h2

基本をしっかりと。これを聞いてからGroovy資料作れたなら楽ができたのにorz。

  • Lambda式の目的は、並列計算の記述を簡単にするため。
    • クロージャとはあえて呼びたくない。なぜなら、ローカル変数をキャプチャしない(実効final)なのでレキシカルスコープとは言えない、関数型(高階型)が無い、など一般的なクロージャと思うとがっかりするよ。
  • Lambda式の「構文」より「並列API(JSR166y、Fork/Join Framework)」の方が重要
  • 2コアのCPUで、100万個の要素の平均分散をparrallellStream()で処理とると半分の時間になる!
    • 100とか200個とかでは意味無いってことね。
  • Lambda式が表すクラスをコンパイルしても.classファイルが生成されない。メモリ上に動的に生成される。またinvokeDynamicが使われている。
  • streamは遅延評価される。
  • 今までstreamは別バイナリだったけどb89でJava8 eaの本体の方に取り込まれ一体化しました。

Type Annotation って何? それを使うとプログラムはどう変わる? #ccc_r53

Java8で新規導入される「型アノテーション」なる言語機能について。
個人的には今回このセッションがツボでした。使いやすいかどうかはともかく、野心的なところがイイね! 個人的要望としてはJava10あたりでASTを変更可能にして欲しいw。

例:

MyObject m = new @Interneed MyObject(); //new対象のクラス
MyObject n = new @NonEmpty @ReadOnly  MyObject(); //new対象のクラス
class Hoge implements @Readonly List<@Readonly String> {} //ジェネリクスの型引数
MyObject xxx = (@NonNull String) myObject; //キャスト
int @ReadOnly[]@ReadOnly[] a = ..
int @ReadOnly[][] a = ..
int []@ReadOnly[] a = ..
  • 何をするものかというと、コード中の型情報に追加情報を指定してやって、それを元に代入やアクセスなどの操作を制限する。実効的にはGroovy 2.1の型チェック拡張みたい。
    • Java8で何らかのASTをアクセスするAPIが提供されるはず。
  • Java8の型アノテーション仕様のサポートは、「型アノテーションを含むコードがコンパイルできますよ」というだけ。驚くべきことにアノテーション自体は1つも標準APIで定義されない。
    • サードパーティさん頼み状態
    • 現時点ではJava8に対応してないが、サードパーティツールの候補一つとして、Java6時代から利用可能な「Checker Framework」というのがある。
      • javaコマンドで実行する。javacではなく。
    • @Interned, @Immutable, @Readonly, ...
  • Checker FrameworkはJava6,7でも実行できるが、以下のような特殊コメントを使う。

//*>>> @ReadOnly...
/*>>> @ReadOnly... */

Groovy2.Xの新機能 #ccc_r25

あてくしの発表。人数多い。JGGUGとは違うのだよ!状態。

しかしまさかのプロジェクタ出力できず。
急遽Windows PCをお借りして2分ロスしてスタート。
PC貸してくださいました方ありがとうござました。
USB接続のRGBコネクタを持ってくるべきだったな。

でも結果としては無事になんとか。(かな?)

失敗から学ぶAPI設計 #ccc_r26

地方における勉強会事情 #ccc_r26

これはツイッターを見てもらった方が良いかも。(ハッシュタグ#ccc_r26)
コミュニティの運営の問題として、様々な話が聞けました。
子供連れイベントいいですね。Scratchとかプログラミンのハンズオンとか。

LT

いろいろ楽しかったです。しかし酒が入ったので記憶があんまりない。

  • 自己生成コードGolf
  • DTrace
  • 破壊的イノベーションw
  • Matzは中部地方では神
  • アーランと寿司
  • いろふさん
  • Play 1 Javaは小数派だが良いものである
  • javapをiPhoneで動かす(違
  • ニセコで寿司?

たいへんおつかれさまでした。運営のJJUGのみなさん、ありがとうございました。是非また参加したいです。

G*ワークショップZ May 2013 - Spockハンズオン5/17

G*ワークショップZ Spockハンズオンのおしらせ。

Spockを試してみたいかたは是非どうぞ。

NTTソフトウェアはオフィスが移転したので、今回から場所がいつもと違います。品川の同じ並びの別のビルです。

オフラインどう書く第九回の問題をJava 8 Lambdaでやってみた。

という記事を書きましたが、今回はJava 8 Lambda式を使って書きなおしてみました。

import java.util.*;
import java.util.function.*;
import java.util.stream.*;

class BusJava8 {
  
  int ceil10(int n) { return (int)(Math.ceil(n/10) * 10); }
  
  int half(int n) { return ceil10(n/2); }
  
  List parse(String input) {
    String[] tmp = input.split(":", 0);
    String p = tmp[0];
    String list = tmp[1];
    return Arrays.asList(Integer.parseInt(p), Arrays.asList(list.split(",", 0)));
  }
  
  int calc(int basePrice, List<String> passengers) {
    Map<String,Function<Integer,Integer>> map = new HashMap<>();
    map.put("An", (Integer it)->it);
    map.put("Ap", (Integer it)->0);
    map.put("Aw", (Integer it)->half(map.get("An").apply(it)));
    map.put("Cn", (Integer it)->half(map.get("An").apply(it)));
    map.put("Cp", (Integer it)->0);
    map.put("Cw", (Integer it)->half(map.get("Cn").apply(it)));
    map.put("In", (Integer it)->half(map.get("An").apply(it)));
    map.put("Ip", (Integer it)->0);
    map.put("Iw", (Integer it)->half(map.get("In").apply(it)));
    Map<String,List<List>> groups =passengers.stream()
      .map(it -> Arrays.asList(it, map.get(it).apply(basePrice)))
      .collect(Collectors.groupingBy(it -> (((String)it.get(0)).substring(0,1))));
    long size = (long)(groups.get("I").size()-groups.get("A").size()*2);
    groups.get("I").sort((a, b) -> (Integer)(a.get(1)) - (Integer)(b.get(1)));
    List<List> tmp = new ArrayList(groups.get("A"));
    tmp.addAll(groups.get("C"));
    tmp.addAll(groups.get("I").stream().limit(size).collect(Collectors.toList()));
    return tmp.stream().mapToInt(it->(Integer)it.get(1)).sum();
  }
  
  static void test(String input, String answer) {
    Java8 b = new Java8();
    List tmp = b.parse(input);
    int basePrice = (int)tmp.get(0);
    List<String> passengers = (List<String>)tmp.get(1);
    int expected = Integer.parseInt(answer);
    int result = b.calc(basePrice, passengers);
    assert result == expected;
  }

  public static void main(String[] args) {
      test("1480:In,An,In,In,In,Iw,Cp,Cw,In,Aw,In,In,Iw,Cn,Aw,Iw", "5920");
  }
}

上では、自分がJava8に全く不慣れなため、非効率・冗長な記述をしているところがあるかもしれません。また、そもそも元のクロージャを駆使したアルゴリズムがGroovy向きだった、など比較としてはフェアではないかもしれませんので、ご留意を。Java 8Lambdaの仕様も未確定だと思いますので、現時点でのeaでの仕様であることも注意ください。

で、感想を言うと、こりゃ書くのたいへんだわ、です。Java8の補完対応IDEを使わなかったこと、などもあると思いますが…。あと結構エラーメッセージが怖いです。たとえば

Java8.java:28: エラー: 不適合な型: 推論変数Rには、不適合な境界があります .collect(Collectors.groupingBy(it -> (((String)(it.get(0))).substring(0,1))));
^
等価制約: Map>>
上限: Map,Object
R,Tが型変数の場合:
メソッド collect(Collector)で宣言されているRはObjectを拡張します
インタフェース Streamで宣言されているTはObjectを拡張します
INT#1,INT#2がintersection型の場合:
INT#1はObject,Serializable,Comparableを拡張します
INT#2はObject,Serializable,Comparableを拡張します

とか。型指定を1つ間違えただけで上のメッセージは…。

他に、Groovyと比べると、例えLambdaを使っても、

  • オペレータオーバーロードが無い
  • Collectionとstreamが別ものである
  • リストやマップリテラルが無い
  • 破壊的ではないリストの結合とかsortとかが無い(見付けられなかっただけかも…)ようなので、メソッドチェインが途切れてしまうのも気になります

などにより、冗長性は高くなるようです。シンプルなケースを除き、高階関数をバリバリ使って関数型ライクに書くのは余り積極的になれない気がしました。リリースまでには多くが解決されてしまうかもしれませんが*1

Lambda式の良い点は、GroovyのCompileStticに比べても、型推論の精度が高いということ。GroovyのCompileStaticでは、クロージャの戻り値の型は推論に寄与してないようで、チェインすると2個目以降に明示指定が必要になります。

さて、速度はどうだったか、などはJJUG CCC 2013 Springのセッションにて(ステマ)。

*1:まあ、lambdaの主眼はマルチコア細粒度並列処理と言われているので、もしそうなら通常の処理にあえて使う必然性もないわけですけれども。

オフラインどう書く第九回の問題をCompileStatic指定してやってみた。

オフラインどう書く第九回の問題をやってみたという記事を書きましたが、今回はCompileStaticアノテーションを使って書きなおしてみました。

import groovy.transform.*

@CompileStatic
int ceil10(int n) { (Math.ceil(n/10) * 10) as Integer }

@CompileStatic
int half(int n) { ceil10(n/2) }

@CompileStatic
List parse(String input) {
  String[] tmp = input.split(':')
  String p = tmp[0]
  String list = tmp[1]
  return [Integer.parseInt(p), list.split(",") as List]
}

@CompileStatic
int calc(int basePrice, List<String> passengers) {
  Map<String,Closure> map
  map = [An:{int it->it},
         Ap:{int it->0},
         Aw:{int it->half((int)map.get('An')(it))},
         Cn:{int it->half((int)map.get('An')(it))},
         Cp:{int it->0},
         Cw:{int it->half((int)map.get('Cn')(it))},
         In:{int it->half((int)map.get('An')(it))},
         Ip:{int it->0},
         Iw:{int it->half((int)map.get('In')(it))},
        ]
  Map<String,List> groups = passengers.collect{String it -> [it, map.get(it)(basePrice)]}.groupBy{List<String> it -> it[0][0]}.withDefault{[]}
  return ((groups.get('A')+groups.get('C')+groups.get('I').sort{
            List<Integer> it -> it[1]
           }.take(groups.get('I').size()-(groups.get('A').size()*2))).sum{List<Integer> it -> it[1]}) as Integer
}

@CompileStatic
void test(String input, String expected) {
  List tmp = parse(input)
  int basePrice = (int)tmp[0]
  List<String> passengers = (List<String>)tmp[1]
  assert Integer.parseInt(expected)==calc(basePrice, passengers)
}

test( "1480:In,An,In,In,In,Iw,Cp,Cw,In,Aw,In,In,Iw,Cn,Aw,Iw", "5920" );

マップの要素指定がプロパティ形式でアクセスできなくなったり、クロージャの暗黙引数が省略できないケース(型推論できない場合)があったり、結構冗長になっていることがわかります。ちなみに上記の書き直しのために、staticalizerは役立ちます。

あと正しいTupleが欲しい。

本記事は、JJUG CCC 2013 Springのステマです。

てっとり早くGroovyでJava8のLambdaを使う

Groovy-MLで議論も始まっており、Groovyは将来的にはJava8正式対応するかと思いますが、世の中ではJava8アーリーアクセス版とかで話題になってるので、試してみました。

シンタックスのレベルでは、当然GroovyはLambda式を扱えませんが(決まってないし!)、Lambda式の実装がSAM型のサブクラスのインスタンスであることを踏まえると、以下のように例えばExpandoMetaClassでアダプタを定義してやれば、クロージャでJava8のStreamを叩けるわけです。

import java.util.function.*
import java.util.stream.Stream

class StreamTest {
  static test01() {
    List<String> names = ["hoge hoge", "foo bar", "junji", "uehara"]
    names.forEach{ println it }
  }
  
  static test02() {
    List<String> names = ["hoge hoge", "foo bar", "junji", "uehara"]
    names.stream()
      .filter { it.length() > 5}
      .map {"[" + it + "]"}
      .forEach{ println it }
  }
  
  static main(args) {
    Iterable.metaClass.forEach = { Closure clos -> delegate.forEach(new Consumer(){ void accept(arg){ clos.call(arg) }}) }
    Stream.metaClass.forEach =  { Closure clos -> delegate.forEach(new Consumer(){ void accept(arg){ clos.call(arg) }}) }
    Stream.metaClass.filter = { Closure clos -> delegate.filter(new Predicate(){ boolean test(arg){ clos.call(arg) }}) }
    Stream.metaClass.map = { Closure clos -> delegate.map(new Function(){ Object apply(arg){ clos.call(arg) }}) }
    test01()
    test02()
  }
}

もちろんこんなことしなくても上のレベルはGroovyだけでできるし、無限長コレクションを使いたければGroovy Stream(github)というのもあるんだけど、上はあくまでJdkAPIのStreamをGroovyのクロージャで利用している、というところがポイントです。

先のMLでの議論にあるように、クロージャとLambda式では得失がある*1ので、将来のGroovyにおいて、クロージャとLambda式が併存するのか、クロージャに一本化するべきなのか、その逆か、実装は、などはこれからの議論ということになるのでしょう。

使用したJava8のバージョンは

java full version "1.8.0-ea-lambda-nightly-h4200-20130429-b88-b00"

です。

参考:

*1:例えばGroovyのクロージャにおけるローカル変数のキャプチャは実効finalとかではなくリファレンスを経由してアクセスするので、値を変更できる代りにローカル変数アクセス時のオーバーヘッドがある

RubyライクにGroovyを書く

誰得なエントリ、かつ「これはひどい」ネタ。

まず、以下のようなコンパイラ設定ファイルを書いておき、例えば~/.groovy/config.groovyなどに保存しておきます。

import org.codehaus.groovy.control.*
import org.codehaus.groovy.antlr.*
import org.codehaus.groovy.syntax.*

class Rubylizer extends ParserPluginFactory {
  ParserPlugin createParserPlugin() {
    new AntlrParserPlugin() {
      Reduction parseCST(SourceUnit sourceUnit, Reader reader) {
        def s = reader.text
        s = s.replaceAll(/(?m)^([^(= \t]+[ \t][^(=]+\([^)]*\))/, '$1 {')
        s = s.replaceAll(/(class.*)</, '$1 implements ')
        s = s.replaceAll(/(\bclass[ \t]+.*)/, '$1 {')
        s = s.replaceAll(/\bend\b/, '}')
        s = s.replaceAll(/#(.*)/, '// $1')
        super.parseCST(sourceUnit, new StringReader(s))
      }
    }
  }
} 

withConfig(configuration) {
  source(extension:'grb') {
    configuration.pluginFactory = new Rubylizer()
    ast(Newify)
  }
}

すると、こんな謎のコード(test.grbとでも保存しておく。拡張子重要。)

def foo(arg,n) # comment
   println arg*n
end

class Person < Comparable
  def name
  int compareTo(rhs)
    println "compareTo"
    this.name <=> rhs.name
  end

  def foo()
    @Newify p1 = Person.new(name:"abc")
    @Newify p2 = Person.new(name:"def")
    println p1<=>p2
  end
end

foo("hello!", 3)
@Newify p = Person.new(name:"abc")
p.foo()

が以下のようにGroovyコマンドで実行できます。

% groovy -configscript ~/.groovy/config.groovy test.grb
hello!hello!hello!
compareTo
-1

ちなみにgroovyコマンドに対する-configscriptオプションは、2.1.0-rc-2以降じゃないと使えません。

# configでast(Newify)してるのに@Newifyしないとうまく作用しないのは原因不明です。

オフラインどう書く第九回の問題をやってみた。

id:torazukaさんのところで紹介されていたオフラインどう書くの問題をやってみた。
問題はこちらです。

def ceil10(n) { (Math.ceil(n/10) * 10) as Integer }
def half(n) { ceil10(n/2) }

map = ["An":{it},
       "Ap":{0},
       "Aw":{half(map.An(it))},
       "Cn":{half(map.An(it))},
       "Cp":{0},
       "Cw":{half(map.Cn(it))},
       "In":{half(map.An(it))},
       "Ip":{0},
       "Iw":{half(map.In(it))},
       ]

def parse(input) {
  def (p, list) = input.split(':')
  return [Integer.parseInt(p), list.split(",") as List]
}

def calc(basePrice, passengers) {
  def groups = passengers.
     collect{[it, map[it](basePrice)]}.
        groupBy{it[0][0]}.withDefault{[]}
  return (groups.A+groups.C+groups.I.sort{it[1]}.take(groups.I.size()-(groups.A.size()*2))).sum{it[1]}
}

def test(input, answer) {
  def (basePrice, passengers) = parse(input)
  answer = Integer.parseInt(answer)
  assert answer==calc(basePrice, passengers)
}

/*0*/ test( "210:Cn,In,Iw,Ap,Iw", "170" );
/*1*/ test( "220:Cp,In", "110" );
/*2*/ test( "230:Cw,In,Iw", "240" );
/*3*/ test( "240:In,An,In", "240" );
/*4*/ test( "250:In,In,Aw,In", "260" );
/*5*/ test( "260:In,In,In,In,Ap", "260" );
/*6*/ test( "270:In,An,In,In,Ip", "410" );
/*7*/ test( "280:Aw,In,Iw,In", "210" );
/*8*/ test( "200:An", "200" );
/*9*/ test( "210:Iw", "60" );
/*10*/ test( "220:Ap", "0" );
/*11*/ test( "230:Cp", "0" );
/*12*/ test( "240:Cw", "60" );
/*13*/ test( "250:In", "130" );
/*14*/ test( "260:Cn", "130" );
/*15*/ test( "270:Ip", "0" );
/*16*/ test( "280:Aw", "140" );
/*17*/ test( "1480:In,An,In,In,In,Iw,Cp,Cw,In,Aw,In,In,Iw,Cn,Aw,Iw", "5920" );
/*18*/ test( "630:Aw,Cw,Iw,An,An", "1740" );
/*19*/ test( "340:Cn,Cn,Ip,Ap", "340" );
/*20*/ test( "240:Iw,Ap,In,Iw,Aw", "120" );
/*21*/ test( "800:Cw,An,Cn,Aw,Ap", "1800" );
/*22*/ test( "1210:An,Ip,In,Iw,An,Iw,Iw,An,Iw,Iw", "3630" );
/*23*/ test( "530:An,Cw,Cw", "810" );
/*24*/ test( "170:Aw,Iw,Ip", "90" );
/*25*/ test( "150:In,Ip,Ip,Iw,In,Iw,Iw,In,An,Iw,Aw,Cw,Iw,Cw,An,Cp,Iw", "580" );
/*26*/ test( "420:Cn,Cw,Cp", "320" );
/*27*/ test( "690:Cw,In,An,Cp,Cn,In", "1220" );
/*28*/ test( "590:Iw,Iw,Cn,Iw,Aw,In,In,Ip,Iw,Ip,Aw", "1200" );
/*29*/ test( "790:Cw,Cn,Cn", "1000" );
/*30*/ test( "1220:In,In,An,An,In,Iw,Iw,In,In,Ip,In,An,Iw", "4590" );
/*31*/ test( "570:Cw,Cn,Cp", "440" );
/*32*/ test( "310:Cn,Cw,An,An,Iw,Cp,Cw,Cn,Iw", "1100" );
/*33*/ test( "910:Aw,In,Iw,Iw,Iw,Iw,Iw,An,Cw,In", "2290" );
/*34*/ test( "460:Iw,Cw,Cw,Cn", "590" );
/*35*/ test( "240:Iw,Iw,In,Iw,In,In,Cn,In,An", "780" );
/*36*/ test( "1240:In,In,In,Ap,In,Cw,Iw,Iw,Iw,Aw,Cw", "2170" );
/*37*/ test( "1000:Iw,Ip,In,An,In,In,In,An,In,Iw,In,In,Iw,In,Iw,Iw,Iw,An", "5500" );
/*38*/ test( "180:In,Aw,Ip,Iw,In,Aw,In,Iw,Iw,In", "330" );
/*39*/ test( "440:In,Ip,Cp,Aw,Iw,In,An", "660" );
/*40*/ test( "1270:Ap,In,An,Ip,In,Ip,Ip", "1270" );

最初はenum使おうと思ったんですが、規模が小さいので、まいっか、という感じになります。
「groups.I」みたいにできなくなりますし(ちなみに左記はgroups.get("I")の別記法)。

この過程で、List(もしくはjava.util.Collecition)にgroupByが定義されていないことに気付いたので、(上ではas Listしています)、groovyのjiraに登録てみました。