kmizuの日記

プログラミングや形式言語に関係のあることを書いたり書かなかったり。

Scala Conference in Japan 2013 〜座長の軌跡〜

時間が経つのは早いもので、Scala Conference in Japan(略称ScalaConfJP) 2013 (3/2(土), 3/3(日)) が開催されてからもう2週間です。200名分のチケットが1週間足らずで売り切れ、その後もキャンセル待ちが増え続けた段階である程度の盛り上がりは予想していましたが、当日は予想以上に盛況で、朝一のJonasさんの講演にも100名以上が参加していました (こういうイベントだと、朝早い時間は来る人が少ないのがよくあることです) 。

Twitter上でのつぶやきも盛んに行われ、#scalaconfjp ハッシュタグがTwitterのトレンドを独占し続けた程でした。当日は、普段Twitterを通してしかやり取りを行っていない、Scalaを実用に用いている方々が議論をしている姿が見られたり、Typesafeからのゲストスピーカー4名と国内の技術者が交流している姿が見られたりと、今回のカンファレンスの目的を考えると概ね成功だったのではないかと思います。

既に参加者による感想ブログ/ツイートが多数書かれています。数が多いので、リンクは省きますが、概ね印象は悪くなかったようでほっとしています。また、スタッフの立場からのエントリもいくつか書かれています:

今回、運営スタッフが軒並みこういった大規模イベントの運営に関して経験がなかったため、様々な試行錯誤がありました。今年でノウハウが蓄積できたので、来年開催するときはスムーズに行くのではないかと思います(既に来年があることを前提にしていますが、来年もできればやりたいところです)。

さて、ここまでが前置きです。このイベントの言い出しっぺとして、座長として、一体どういう経緯でこのイベントを企画したのか、どういう時期にどういう考えがあったのか、座長という立場から時系列で書いてみようと思います。長いのでテキトーに読み飛ばしてもらってかまいません。

おまえ何やってたの?

その前に自分が何をやっていたかを簡単にまとめておきます。立場としては一応座長(?)だったのですが、やっていた作業は、以下のような感じです。時期に応じて必要な作業を色々やっていたように思います。

  • Typesafeのゲストとのメールのやりとり
  • 東工大側窓口とのメールのやりとり
  • 会場下見
  • 書類への押印・郵送作業
  • ちょっとヤバそうな人への対応 (謎)
  • ページ更新のアナウンス

あとは、「座長としてここは譲れない」部分に関する決定でしょうか。たとえば、英語話者の発表に関して、同時通訳を当日雇うのはどうかという案が出たことがありました。しかし、これは

  • 発表の醍醐味である動的な部分が損なわれる
  • 雇った同時通訳が、技術的な用語を含めてちゃんと通訳してくれるか不安がある
  • 発表者の負担が増える懸念がある

といった事を考慮して却下しました。

また、キャンセル待ちが大幅に出た段階で、チケットの追加販売の案が出たことがありました。会場のキャパシティには若干余裕はありましたし、不可能ではありませんでしたが、初回開催であることと、弁当の発注量の変更を含め、関係スタッフにかかる負荷がどのくらいになるか予測しづらかったなどの理由もあり、チケットの追加販売は今回は見送ることにしました(チケット買えなくて参加できなかった方、申し訳ありません)。これについては、来年はチケットの販売スケジュールにもっと余裕をもたせて、追加販売をやりやすい体制にしたいところです。

事の発端

今回のカンファレンスの発端となったのは、次のツイートでした。

Scala Days 2012に参加していたので、次の開催地がアジアになるかも、という情報は当日耳にしていましたが、日本誘致という事は考えてもみませんでした。その後、Scala Days 2013を日本に誘致するためには?という議論を経て、Scala Days 2013の日本誘致は難しそうだと考えるに至りました。しかし、その中で、

というツイートをみて、誘致は無理でも、日本でのScala Conferenceならいけるのでは?と考え始めました。この時のJoshのツイートがなければ、今回のカンファレンスを企画しようと本気で考えることは無かったかもしれません。Typesafeからゲストを数名呼ぶというのもこの頃から少しずつ考え始めていました。

構想段階 (2012年6月)

さて、日本でのScala Conferenceをやろうと考え始めたはいいものの、私にはそのようなイベントの運営経験はなく、右も左もわからない状態でした。とりあえず、そういうことは先人に聞くのが一番だろうということで、

  • サイボウズラボの竹迫さんにYAPC::Asia 2008の時の事を伺ったり
  • 会場の候補として考えていた東工大の首藤先生に話を伺う

などして、企画として始動するにはどうすればいいか、少しずつ自分の中で構想を固めていきました。

企画始動前の交渉 (2012年6月)

さて、このちょうどいい時期に、Typesafeの Jamie Allen (@jamie_allen) さんから別件で私宛にメールが来たのでした。今にして思えばこれはとてつもない幸運でした。

というのも、私はScala Days 2010, 2011, 2012と参加して、Typesafe内に数名知人はいたものの、EPFL時代とメンバの役割が大きく変わっている可能性があったため、誰とコンタクトを取るべきか悩んでいたからです。

この別件については省略しますが、返信のついでに彼に

  • 2013年3月に、詳しいプランは決まっていないがScala Conferenceを日本でやることを企画している
  • Typesafeから招待講演者を呼びたい

旨を伝えたところ、比較的好感触な返事が返ってきました。その後数回のやり取りを経て、ちゃんとした企画と渡航費の負担さえできればTypesafeから招待講演者を呼ぶのは十分現実的な範囲になってきたと感じていました(もちろん、その渡航費の負担は決して楽ではなかったのですが)。

企画始動 (2012年7月21日(土))

かなりおおざっぱではあるものの企画の骨子が出来たので、企画の是非および、そのために協力してくれる方がどれくらいいるかを確認するために、Scala Conference in Japan(仮) キックオフと題したイベントをpartake.inに作成し参加者を募集しました。参加者が少ない場合や、企画の趣旨に無理があるという意見が多いようであれば企画は放棄するつもりでした。しかし、幸運にも27名もの方に集まっていただいて、企画にも賛同していただいたため、この日からScala Conference in Japan 2013の企画が正式に始動することになりました。また、月1ペースでオフラインミーティングを行うことも決定しました。

ここから、私はJamieさんは継続的に連絡を取り合い、企画の進捗状況を定期的に報告するようになりました。

停滞期 (2012年8月)

企画が始動したはいいものの、Webページも存在せず、しばらくは全体的に活動は停滞していました。話し合いは主にスタッフ用のclosedなMLで行っていました。この時点での優先事項は会場を確定させることだったのですが、下見日程をスタッフと東工大側の窓口の方でなかなか合わせられなかったこともあり、話し合いのペースも比較的ゆっくりしていました。

会場下見・確定 (2012年8月下旬)

8月下旬にようやく日程を合わせることができ、会場候補である東工大 大岡山キャンパスに初めて下見に行くことになりました。このとき、東工大の窓口の方が大変丁寧に大学構内を案内してくださったのが印象に残っています。また別の会場を探してメールのやり取りをして…となるとかなり時間がかかりそうだったので、大岡山キャンパス内に会場を決めることにして、下見と同時に、会場に良さそうなホール・部屋をこの時期に仮予約しておきました。

Webページ開設 (2012年9月)

こうして施設の仮予約ができたことで、Scala Conferenceのページを公開できるようになりました。この時点では最小限の情報しか載せていませんでしたが、ようやく話が動き出したという感じがしました。8月当時、会場が決まらなかったら…ということで、内心焦っていたことを覚えています。

本格始動 in ML (2012年9月〜11月)

仮予約とはいえ会場が確定したため、この時期からようやく他のスタッフも動けるようになってきました。この期間、主な話し合いはスタッフMLで行われていたため、開催時期が近づいてくるにつれメールの流量が凄まじい量になり、末期は管理できる限界を超えかけていました。途中、"会場", "All" などの「タグ」をSubjectに付けることで個々人が把握する必要があるメールを区別する試みが行われましたが、膨大なメールの中に埋もれてしまい、必要な情報を検索するのがかなり手間でした。

Typesafeからのゲストについては、私が中心になって交渉を進めていました。比較的早い内に、4名のゲストは固まっていたものの、渡航費を全額支払える保証がこの時点ではなかったこともあり、しばらくは「暫定的」な状態でした。スポンサー募集については、ページで公募する前に色々模索していたのですが、最初のスポンサー企業様を獲得するまでは、スポンサーチムを含め、苦労が大きかったように思います。また、この時期、スポンサーチームの方が本業の方が多忙になってしまったため、事務局チームにスポンサーとの交渉をお任せすることになりました。

この辺りで、

  • 一人のスタッフへ負荷が罹りすぎないようにするにはどうすればいいか(チーム内での負荷分散)
  • ML + github Wikiより良い情報共有/議論の場

について考え始めていました。ただ、スポンサー対応にしろ、他の作業にしろ、チームで分担するより結局は個人でやった方が効率が良いため、今思えば「チーム内での負荷分散」という考え方に私が拘ったのは良くなかったと思っています。この頃は、どうすればいいのか日々考えて心労がたまっていたように思います(笑)。

また、ML + github Wikiでの議論はもはや管理できる限界を超えてしまっており、このままではまずい、とも感じていました。

co-meeting の導入 (2012年12月)

MLでのやり取りに(おそらく)皆限界を感じていたなか、12月の定例ミーティングで、Sawayamaさんがco-meetingを使ってはどうかという提案をしてくれました。私も別件でco-meetingを使ったことがあり、現状のMLベースでのやりとりの問題を解決するにはとても良さそうだと考えたため、その日の内にco-meetingを導入することが決まりました。早速、中核となっていたスタッフは全員co-meetingに入り、凄い勢いでスタッフ内でのコミュニケーションがco-meetingに置き換えられていきました。

Webページリニューアル(2012年12月〜2013年01月)

初期公開時のWebページはデザインを含め、色々問題があったのですが、@seratchさんが、複数ページ対応なども兼ねて、内部構成を Jekyll ベースに整理してくれました。このおかげで、後のページ更新作業が格段に楽になったように思います。また、細かい見た目の調整についても、かなりの部分を@seratchさんに手がけていただきました。ページ更新の頻度がその後どんどん増えていったことを考えると、この時期にページ構成を再編したのは大正解でした。

個人ベースの作業分担 (2013年1月〜)

co-meetingの導入によって、チーム内での情報共有・チーム間での情報交換は格段にやりやすくなりましたが、依然として少数のメンバに作業負荷がかかるという状態が続いていました。特に、私が

  • 座長としての作業 (請求書への押印・郵送)
  • Typesafeのゲストとのメールのやりとり
  • 会場チームリーダーとしての作業 (この時期までは主に東工大側とのメールのやり取り)
  • タイムテーブル・プログラム作成

を兼ねていたため、私がボトルネックになりかけていました。幸いにして、

引き継いでもらったため(作業を奪っていったともいう(笑))、私がオーバーフローすることなく、座長としての役割に専念できるようになっていきました。その他発生したタスクについては、個人がそれぞれのタスクに責任を持つ、個人ベースの作業分担になっていきました。これは私が割り振ったのではなく、スタッフの各々が自分で作業を受け持ってくれたようなもので、本当に助かりました。

カンファレンス参加者募集開始 (2013年2月〜)

決済にDoorkeeperを利用するというのは事前に決まっていたため、これに関しては比較的スムーズに行ったように思います(Sawayamaさんが裏で苦労していたかもしれませんが…)。ただ、1月の定例ミーティングの時点では、参加費3000円のScalaというメジャーとは言えない言語のカンファレンスで、200名枠が埋まるかどうかについてはスタッフの多くが不安に考えていたようです。

私は過去に類似イベントに多数参加した経験から、この手のイベントには、Scalaを仕事で使っている人だけでなく、Scalaを勉強し始めた人や、Scalaにはあまり興味がない「一見さん」もそれなりに参加すると考えていたので、最終的には200名枠は十分埋まるだろうと考えていました。結果としては、私の予想も遥かに上回って、1週間足らずでチケットが完売してしまったわけですが。

最終作業 (2013年2月中旬〜)

この時期は本当に大詰めといった感じで、定例ミーティングでも

  • 前日・当日の作業リストアップ
  • 緊急時の対応
  • ノベルティの補完場所
  • 翻訳チームとの連携(スライド同時通訳)
  • 招待枠の最終確認

といった作業が中心でした。この時期には、本当に座長として必要な作業以外は引き取ってもらっていたので、私自身の作業量は逆に減っていました。この時期は、本当に開催されるんだ、という実感でスタッフ皆がみなぎっていたように感じました。

前日作業 (2013年3月1日)

この日は、前日動けるメンバーだけが集まって、

  • 看板・机の借り受け作業
  • バッグにスポンサー企業様からのノベルティを入れる作業

といったことを行っていました。実は、とある別のイベントの実行委員から聞いたところによると、前日はかなりぎりぎりだったらしいのですが、幸いにして、前日準備は比較的早めに終わりました。

当日 (2013年3月2日)

当日の座長の仕事は飾りになることです。というわけで、お世話になった方々への挨拶周りが私の当日の作業でした。「座長」などという立場で、これだけの規模のイベントでオープニングスピーチなどやったことがないので緊張しまくりでしたが、参加者の皆さんにはどのように見えたでしょうか。後で動画をみると自分のアレさに卒倒してしまいそうで少し怖いです(笑)。ちなみに、この日は、スーツコス+ScalaConfJP スタッフパーカーという謎の格好をしていました。一応、会場を回って何か起きていないか確認したりもしていましたが、スタッフの方々のおかげで、こころおきなく、座長の責務(?)を全うすることができました。

ちなみに、懇親会で胴上げをされたのは想定外でちょっと怖かったです(笑)が、同時に達成感もあり、非常に嬉しかったです。

この日のスタッフの方々の働きは素晴らしかったです。参加者の方々にとって至らないところもあったかもしれませんが、それについてはアンケートに記載されている分も含めて、きちんと来年にフィードバックしたいと思います。

ハッカソン大会 (2013年3月3日)

ハッカソン大会は、無料イベントで申し込みも当日まで誰でもOKという感じだったので、私も座長というより一参加者として楽しむことができました。合間合間に好き勝手に参加者がtalkしたり、もくもくとコーディングしたりと、フリーダムな雰囲気が出ていたのではないかと思います。ただ、この前日でかなり気が抜けていたこともあり、ちょっとだらしなかったかもしれません。Typesafeからのゲストの内、Jonasさんを除く3名にも来てもらうことができ、活発な議論が行われていました。最後には、プチプログラミングコンテストが行われ、Typesafeのゲストによる審査の結果、優勝した@cocoa_rutoさんには、Joshuaさん直筆サイン入りの『Scala in Depth』が贈呈されました。

この日は、ハッカソン大会にも参加した一部スタッフを交えて懇親会があったのですが、疲労コンパイルだったので、最初1時間くらいだけ出て帰ることにしました。本当の事を言うと、最後まで居たかったですが。

まとめ

と、このように時期によって色々問題を抱えつつも、カンファレンスは盛況の内に終わることができました(まだ後処理は残っていますが)。これもひとえに、中核になって動いてくれたスタッフの方々、Typesafeからのゲストを招待するに当たっての原資となったスポンサー企業様、発表者の皆様、参加者の皆様のおかげです。あらためてお礼申し上げます。

sbt + sbt-idea + Intellij Scala Pluginを使ったScala開発 (in Mac OS X)

注意:この記事の内容は、2013年7月2日現在、やや古くなっています。現在は「Use external build」のチェックはONにしたままで、「Use project FSC」をチェックしない方が安定して動作します。これは、最近のアップデート(ここ1〜2ヶ月くらいの話ですが)「Use external build」を使ったときに、IDEAが内部でzincというsbtベースのスタンドアロンコンパイラを内部で使うようになった事に起因しています。コンパイラの設定以外は現在でもそのまま通用しますが、このエントリを読んでIntelliJ IDEA + ScalaプラグインによるScala開発を行おうとしている人は注意してください。

これは、Scala入門をIntelliJ IDEAでやろうとしている人向けのエントリではありません。このエントリでは、IntelliJ IDEAのScalaプラグインのインストールやその他プラグインのインストールについては触れていません。また、設定上の注意事項(特にPlay 2と併用する場合)などは飛ばしているので、ご注意ください。

少しは役に立つ記事を書いてみようかと思い、ちょっと普段Scala + sbt + IntelliJ IDEA + Scala Pluginでどいういう風に開発してるのか書いてみることにしました。面倒なので、sbtの設定ファイルについての解説は飛ばします。テンプレートとなるプロジェクトを作成したのでこれをforkするなりcloneするなりして弄ってみてください。

さて、とりあえず上記のscala-idea-templateプロジェクトをcloneしてきて、sbtがインストールされているとします。コンソールからsbtコマンドをたたくと次のように表示されます。

 ~/git/scala-idea-template (git master) 
[mizushima]$ sbt
[info] Loading global plugins from /Users/mizushima/.sbt/plugins
[info] Loading project definition from /Users/mizushima/git/scala-idea-template/project
[info] Updating {file:/Users/mizushima/git/scala-idea-template/project/}default-2c9cb0...
[info] Resolving org.scala-sbt#precompiled-2_10_0-m7;0.12.1 ...
[info] Done updating.
[info] Set current project to sbt-idea-template (in build file:/Users/mizushima/git/scala-idea-template/)
> 

さて、ここで

[info] Loading global plugins from /Users/mizushima/.sbt/plugins

というメッセージはとりあえず無視してください(こちらの開発環境での設定によるものです)。ここで、既にsbtのプラグイン設定ファイル(project/plugins.sbt)にIntelliJ IDEA Scalaプラグイン用のプロジェクト設定ファイルを生成するためのsbt-ideaプラグインが入っているので、これを実行します。

> gen-idea
[info] Trying to create an Idea module sbt-idea-template
[info] Updating {file:/Users/mizushima/git/scala-idea-template/}default-986503...
[info] Resolving org.specs2#specs2-scalaz-core_2.9.2;6.0.1 ...
[info] downloading http://repo1.maven.org/maven2/org/specs2/specs2_2.9.2/1.12.2/specs2_2.9.2-1.12.2.jar ...
[info] 	[SUCCESSFUL ] org.specs2#specs2_2.9.2;1.12.2!specs2_2.9.2.jar (6901ms)
[info] Done updating.
[info] Resolving org.specs2#specs2-scalaz-core_2.9.2;6.0.1 ...
[info] downloading http://repo1.maven.org/maven2/org/specs2/specs2_2.9.2/1.12.2/specs2_2.9.2-1.12.2-sources.jar ...
[info] 	[SUCCESSFUL ] org.specs2#specs2_2.9.2;1.12.2!specs2_2.9.2.jar(src) (1718ms)
[info] downloading http://repo1.maven.org/maven2/org/specs2/specs2_2.9.2/1.12.2/specs2_2.9.2-1.12.2-javadoc.jar ...
[info] 	[SUCCESSFUL ] org.specs2#specs2_2.9.2;1.12.2!specs2_2.9.2.jar(doc) (3803ms)
[info] Excluding folder target
[info] Created /Users/mizushima/git/scala-idea-template/.idea/IdeaProject.iml
[info] Created /Users/mizushima/git/scala-idea-template/.idea
[info] Excluding folder /Users/mizushima/git/scala-idea-template/target
[info] Created /Users/mizushima/git/scala-idea-template/.idea_modules/sbt-idea-template.iml
[info] Created /Users/mizushima/git/scala-idea-template/.idea_modules/sbt-idea-template-build.iml
> 

こんな感じのメッセージが表示されるはずです。ちなみに、build.sbtで依存関係に指定したscalaj-httpがダウンロードされていませんが、これは既に他のプロジェクトで同じライブラリの同バージョンのものがダウンロードされてキャッシュされているからです。

さて、ここまででIntelliJ IDEAの設定ファイルは生成できたので、IntelliJ IDEAを起動します。

http://gyazo.com/1dab70c5fc4277b8a7c9a4f1e88707b5.png

こんな感じの画面が表示されるので、「Open Project」を選択して、scala-idea-templateのあるディレクトリを指定して、「Choose」を押します(以下)。

http://gyazo.com/60b679810a2a2232cea90d1b9684b5bf.png

ちょっと待つと、以下のような画面が表示されます。

http://gyazo.com/429d5ef17c9f2e3b59714c8b90f3db83.png

これで準備完了…の前に、もう一ステップ必要です。src/main/scalaがScalaソースのルートディレクトリに設定されているので、そのためのディレクトリを作ります。

http://gyazo.com/0ea529a245b5393121566fd624fb711d.png

これで今度こそ準備完了です。とりあえず、パッケージを作ってみましょう。面倒なので、toolsパッケージにします。

http://gyazo.com/5d854395703e7cc329a4df751259e556.png

toolsパッケージの上にHello objectを作成します。

http://gyazo.com/e8bc878f92e511ca6dce4e0e059eb95f.png

あとは適当にプログラムを書いていきましょう。IntelliJ IDEA の Scala プラグインは import補完も含めてちゃんとサポートしています。例えば、java.util.Calendarが使いたくなったとします。以下のようにCalendarまで入力して、Alt + Enter。

http://gyazo.com/cf5fd84124bc35b66184d5a985399717.png

すると、以下のような感じで、java.util.Calendarを自動的にimportしてくれます。

http://gyazo.com/85c22014f9923abf6d45f594b47d05b1.png

val calendarの型が知りたい場合、calendarにカーソルを合わせたときに出てくる電球マークの「Add type annotation to value definition」を選択すると、以下のようにcalendarの型としてCalendarが明示的に付きます。型推論の結果がよくわからないときに便利です。

http://gyazo.com/5fffcf1ff30fd0e912e0d097e9b98cbf.png

さて、プロジェクトをビルド…する前にちょっと注意点をば。現在の最新版のIntelliJ IDEA + Scala Pluginでは、以下の「Use external build」のチェックボックスをまず外す必要があります。

http://gyazo.com/aea80f6643e97e9d123038981531c6fa.png

プロジェクトの設定画面が次のようになっていることを確認し、「Use project FSC」にチェックを入れます。さて、ビルドしてみましょう。ビルドは、「Build」メニューの「Build Project」を使います(私は、Command + Bでビルドできるようにしてあります)。

http://gyazo.com/6e4b55d7cd4f20d79e7a22656a318900.png

Scalaはコンパイルが遅い言語の一つですが、IntelliJ IDEA + Scala Pluginを使った場合、差分ビルドではsbtでビルドするよりも高速にビルドできます(厳密に測定していませんが、差分ビルドでは明らかに早い)。

IntelliJ IDEA (+Scala Plugin)は、他のIDEに慣れていると若干操作性に癖を感じるかもしれませんが、

  • リファクタリングサポート(特にRename)
  • 自動インポート
  • メソッド/変数の(定義元/呼び出し元)へのジャンプ
  • sbt-ideaによって、依存ライブラリのソースコードがダウンロードされる

など、現代的なIDEに必要な機能はあり、現時点でのScala開発環境としては最も良く出来たものの一つだと思います。もちろん、Eclipse JDTや、IntelliJ IDEA (Java)における補完機能には及ばない部分もありますが、リファクタリングサポートや自動インポート、依存ライブラリのソースへのジャンプなどの機能があるだけで結構効率が違うと感じます。

さて、テストの話も書いてみようかと思ったのですが、そろそろ眠くなって来たので、今日はこれまで。

irof式が実装できたよ。そう、Scalaならね! (Re: irof文が実装できたよ。そう、Scalaならね!)

See: irof文が実装できたよ。そう、Scalaならね!

返り値が思い通りにならないことに困ってらっしゃったようなので(?)、実装してみました。

object Main {
  case class irof[A](val condition: Boolean)(blockA: => A) {
    val thunk: () => A = () => blockA
    def elof[B <: A](blockB: => B): A = if(condition) thunk() else blockB
  }

  implicit def irofToA[A](i: irof[A]): Any = if(i.condition) i.thunk()

  def main(args: Array[String]) {
    val x: String = irof(args.length > 0) {
      "foo"
    } elof {
      "bar"
    }
    println(x)
  }
}

これの問題点は、elofの型制約のせいで、

val x: Any = irof(args.length > 0) {
  "foo"
} elof {
  1
}
 println(x)

などが通らない事ですが、たぶん解決するのが難しい…(頑張れば解決できるかも)

Weak Conformanceのはなし

この記事はPlay or Scala Advent Calendar 2012 の 6日目です。今回の記事は、とりわけ役に立たないことを自負しています(役に立つ記事を読みたい人は飛ばしてください)。

本題に入る前に、conformanceという概念についてざくっと簡単に説明しておきます。これは、(Scalaの言語仕様では)サブタイプ関係とだいたい同じです。詳しくは、Scala Language Specification 3.5.2 を参照してください。

さて、このconformanceに関係した概念としてScala 2.8ではweak conformanceというものが導入されました。というと、何やら難しく聞こえますね。でも、実はとても簡単な話で、単にJavaのプリミティブな数値型相当の型(Byte, Short, Int, Long, Float, Double)同士の関係を、通常のconformanceとは別に定めただけのものです。

Scala Language Specificationから抜粋しますが、weak conformance( <:w)は次のように定義されています。

  • Byte <:w Short
  • Short <:w Int
  • Char <:w Int
  • Int <:w Long
  • Long <:w Float
  • Float <:w Double

さて、これを見るとわかりますが、Javaのプリミティブ型同士の代入互換性に関する規則と酷似しています。なんでこのような規則をわざわざ導入したのでしょうか。

実は、Scala 2.7まではJavaのプリミティブ型相当の型同士の変換はimplicit conversionを使ってPredefに定義されていました。言語仕様上、これらの型同士の互換性については特別に定義していなかったわけです。これは、Scalaがプリミティブ型を排除した事を考えれば妥当な判断と言えるでしょう。

Scala 2.8で再びこのような「プリミティブ型」を特別扱いするような規則を入れたのは一体何故なのでしょうか。一言でいうと、「Scala Language Specification Changes in Version 2.8を読め」で終わります。でも、それで終わっても仕方が無いので説明してみます。

weak conformanceが導入された主な理由は、条件式 (if)やmatch式、try式における型の推論結果をより期待された形にするためです。

具体例として、次のようなif式についてみてみましょう。

if (cond) 1 else 1.0

Scala 2.8未満(Scala 2.7.7まで)では、この式の型はAnyValと推論されます。これは、1(Int)と1.0(Double)の間には型としては関係がなく、共通の「最も近い」祖先(ここで、「祖先」はそれぞれの型自身も含むものとする)がAnyValだからです。

一方、Scala 2.8では、この式の型はDoubleと推論されます。これはどういうことかというと、Scala 2.8では条件式

if (cond) expr1 else expr 2

の型は、expr1とexpr2の型のweak least upper boundになるように仕様が変更されたからです。このweak least uppper boundを計算するために、先ほどのweak conformance (<:w)関係が使用されます。weak least upper boundというと何やら難しそうですが、これは単にweak conformance関係をconformance関係に追加したものです。要は、

AnyVal
+ Double
   + Float
      + Long
         + Int
            + Char
            + Short
               + Byte

という継承関係があるものとして、共通の「最も近い」祖先型を探すだけです。さて、

if (cond) 1 else 1.0


をもう一度見直してみましょう。1の型であるIntと1.0の型であるDoubleについて、 Int <:w Doubleが定義されているため、IntとDoubleのweak least upper boundはDoubleになります。

match式やtry式についても同様です。たとえば、以下の式の型は、Scala 2.7.7ではAnyVal、Scala2.8以降ではIntと推論されます。

{
  val a = 1; 
  a match { 
    case 1 => 1
    case 2 => '2'
    case 3 => 3.toShort 
  }
}

ここで注意しておかなければいけないのは、あくまでweak conformanceは「weak」 conformanceであるということです。これはたとえば、以下のように明確なconformance関係を要求する場合、weak conformance関係が定義されていても意味がないということです。

def foo[T <: Double](x: T): T = x
foo(1.0) //OK
foo(1) //inferred type arguments [Int] do not conform to method foo's type parameter bounds [T <: Double]

さて、この記事の最初で役に立たない事を自負していると書いた通り、この記事の内容を理解したからといって、実際のScalaコードを書く際にはほぼ役に立ちません。あえていうなら、Scala 2.7.7をまだ使っている人がScala 2.8以降(実質的にはScala 2.9.Xになるでしょう)を使い始めるときは少し役に立つかもしれません。ですが、ふつーにScalaを使っている範囲で、この仕様の有無を知っていようがいまいがまず問題にならないでしょう。

というわけで、Weak Conformanceのはなしは以上です。

Iterator.continually()を使おう

Scalaの標準IOライブラリであるscala.io(.Source)は非常に腐ってます。読み込みしか対応してない上に、バイト列の読み込みもサポートしてないという代物。しかも、Scala 2.7まではscala.io.Sourceがそのままだとcloseできなかったりとか(今はcloseできます)。さっさと退場して欲しい代替ライブラリが標準になって欲しいところなんですが、現状の情勢をみるにしばらく先になりそうな感じです。

というわけで、Scalaを使うならJavaのIOライブラリとお付き合いしなければいけません。とりあえず、Commons IOとか使うのも良いかもしれませんが、ちょっとしたものを書くときにいちいちCommons IO使うのも面倒くさいです。

じゃあ、BufferedReaderとかをいちいちwhileループで回すのかといえばそれも面倒くさいです。そこで、その苦痛を緩和してくるのが、Iterator.continually()です。使い方は簡単。なんかループしたい処理をIterator.continually()の引数に渡すだけです。試しに標準入力から一行ずつ読み取って、行番号付きで出力する処理を書いてみましょう。

val lines = Iterator.continually(readLine()).takeWhile(_ != null).toList
for ((line, lineNum) <- lines.zipWithIndex) {
  printf("%d:%s%n", lineNum + 1, line)
}

たったこれだけです(もうちょっと短くできますが、それはおいときます)。Iterator.continually()は、引数で与えられた処理を無限回繰り返すIteratorを返してくれるので、呼ぶたびに返り値が変わる入力系のメソッド/関数と相性が良いです。もちろん、無限に読み取られても困るので、どっかでぶった切ってあげる必要があります。それがtakeWhile(_ != null)の部分で、これを入れることによって、readLine()がnullを返した時点で終了するIteratorが返ってきます。後は、それをtoListやらtoSeqでimmutableなシーケンスに変換してあげるだけ。簡単ですね。もちろん、readLine()以外にもこれは応用できます。たとえば、標準入力から1バイトずつ読み込んで配列に変換して、文字列として出力する処理は次のように書くことができます。

val bs = Iterator.continually(System.in.read()).takeWhile(_ != -1).map(_.toByte).toArray
println(new String(bs, "US-ASCII"))


なお、ほとんど同じようなことをしてくれるメソッドにStream.continually()がありますが、メソッドや関数内で完結する処理に対して、あえてこちらを使う必要はないと思います。Iterator.continually() 対 Stream.continually() の議論については、こちらを参照してください。

Jsonda 0.4.0リリース

昨日、Jsonda 0.4.0をリリースしました。先日の日記では、次のバージョンを0.3にするようなことを書いたのですが、リリース版のメジャーバージョンは0.2 -> 0.4 -> 0.6のようにすることにしました。

Jsonda 0.2.1と同じく、Scala 2.9.1とScala 2.9.2用にクロスビルドしています。sonatypeにpublishしているので、以下の一行をbuild.sbtに追加するだけで使えます。

libraryDependencies += "com.github.kmizu" %% "jsonda"  % "0.4.0"

0.4の主な変更点は、Jsonda DSLのコアを特定のJSONライブラリ非依存にしたことです。これによって、lift-json、json4s、scala.util.parsing.jsonのオブジェクトを同じDSLで構築できます。

たとえば、

import com.github.kmizu.jsonda.dsl.LiftJsonDSL._

val person = %{
  'name :- "Kota Mizushima"
  'age :- 29
}

とした場合、lift-jsonのJsonAST.JObjectが返ってきます。また、

import com.github.kmizu.jsonda.dsl.StdJsonDSL._

val person = %{
  'name :- "Kota Mizushima"
  'age :- 29
}

とした場合、scala.util.parsing.jsonのJsonObjectが返ってきます。他にもjson4sにも対応していますが、これは今のところlift-jsonとほぼ同じなので省略。

また、Option型にも対応しました。これによって、

val hoge = %{
  'foo :- Option(100)
  'bar :- None
}

とした場合、

{"hoge":100, "bar":null}

というJSONに相当するオブジェクトが作られるようになりました。

その他には、テストケースの追加、null絡みのバグをいくつか修正しました。Jsondaの開発では、これまでGithubのIssues等のIssue Trackerを使ってなかったせいで直した不具合が曖昧になっていました。これはリリース内容書くときにも不便だということに気づいたので、これからはGithubのIssuesに不具合を登録していきます。

Jsonda 0.2.1 & Jsonda 0.3について

しばらく前ですが、Jsonda 0.2.1をリリースしました。0.2からScala 2.9.1とScala 2.9.2のクロスビルドしているので、どちらでも使えます。

Jsonda 0.2からsonatypeにpublishしてるのでresolversへの追加なしで、以下の一行をbuild.sbtに追加するだけで使えます。

libraryDependencies += "com.github.kmizu" %% "jsonda"  % "0.2.1"

JsondaはScalaでJSONオブジェクトを構築するための汎用DSLライブラリです。Jsondaを使えば簡潔で読み易い記述でJSONオブジェクトを構築できます。以下は例です。

import com.github.kmizu.jsonda.Implicits._

val person = %{
  'name :- "Kota Mizushima"
  'age :- 28
}

不完全ですが、ドキュメントも少しずつ更新しています。

0.2.1では、lift-jsonのみ対応です。現在開発中の0.3では、coreはlift-json非依存になっており、lift-jsonもscala.util.parsing.jsonも同じDSLで取り扱えるようになっています。json4s対応を済ませたら、0.3出そうかと思っています。