2014年7月28日月曜日

Scalaz勉強会しました

APPAREL CLOUDアパレルウェブが提供しているアパレル向けのクラウドサービスです。ボクが所属するEverforthの提供するEverforthプラットフォーム上に構築されています。
Everforthプラットフォームは、O2Oやオムニチャネル向けのクラウド・プラットフォームです。Everforthプラットフォームでは、サービスを実行するクラウド実行基盤と同時にO2Oやオムニチャネル・ビジネスをサービスに結び付けるための業務分析、システム分析を包含したサービス開発体系の提供も予定しています。
「サービス開発体系」は、オブジェクト指向ビジネス・モデリング、オブジェクト指向分析設計の技術をベースに、Everforthプラットフォームをターゲットとしたアプリケーション開発の方法論として整備していく予定です。
一般的なオブジェクト指向分析設計方法論が複雑で分かりづらいものになっているのは、どの業務分野、どの実装技術、どの実行基盤に対してもニュートラルな形で整備されているため、(1)汎用性を担保するために複雑化する、(2)ターゲットの業務分野、実装技術、実行基盤むけにカスタマイズが必要、という要因があると思います。
逆に、業務分野、実装技術、実行基盤を確定した上でカスタマイズすればかなりスリムなものにできるはずです。
Everforthサービス開発体系では、業務分野はO2O/オムニチャネル、(バックエンドの)実装技術はScalaによるOFP(Object-Functional Programming)、実行基盤はEverforthクラウドプラットフォームとすることで、この問題を解決しようとしています。

勉強会

Everforthサービス開発体系を業務に載せるためには、エンジニアの技術教育が非常に重要になってきます。
そこで上流のビジネス・モデリングからScalaでの実装にいたるまでの一連の技術の入門編を企画しました。これらの技術は連動しており、業務分析からオブジェクト指向分析/設計を経由してScalaでの実装まで一気通貫の体系になっています。
  • 実務者のためのかんたんScalaプログラミング
  • 実務者のためのかんたんScalaz(第1回)
  • 実務者のためのかんたんScala入出力プログラミング
  • 実務者のためのかんたんScala設計
  • 実務者のためのかんたんオブジェクト指向分析/設計
  • 実務者のためのかんたん業務分析
第1回は7月7日に札幌で開催しました。
札幌が東京と並んでEverforthの開発拠点になっているので、まず札幌で勉強会として開始しています。
今回は技術体系をざっくりおさらいするのが目的で、Eveforthプラットフォームに依存した部分もないので、オブジェクト指向技術やScalaの普及という目的もあり、一般の方も参加できる形にしています。

Scalaz

第1回「実務者のためのかんたんScalaz」の資料は以下になります。


最初のテーマとしてScalazを選んだのは、社内チャットでScalazについての質問があったのがきっかけになっていますが、関数型プログラミング導入の狙いの説明を最初にしておくのが有益ではないかという判断もあります。Scalazは「純粋」関数型プログラミングのためのライブラリなので、関数型プログラミングの方向性や特性をより際立った形で取り上げることができます。
かんたん?
今回の資料で「かんたん」なのは、スライドの右上に「かんたん」のマークがついている所で、以下の2つの要素です。
  • Scalazの便利機能
  • Monoid
それ以外は、なんとなく聞いておいてもらえばよい、というレベルの情報として考えています。
逆に「Monoid」についてはこの機会にプログラミングに取り入れて欲しい、という意図から「かんたん」の範囲に含めています。「Monoid」というととっつきにくく感じますが、プログラミングのイディオムとしては簡単で利用範囲の広い汎用テクニックであり、関数型プログラミングの考え方をマスターする起点にもなるという、一粒で二度も三度もおいしいものです。
Scalazを本格的に取り上げる場合は、Traversable・Foldable、Promise・Task、Free、Treeといった所もテーマにしたいのですが、今回は「かんたん」ということで断念しました。機会があればこの辺りも加味した、Object-Function Programmingという切り口でまとめてみたいと思います。

次回以降の予定

第2回は「実務者のためのかんたんScala設計」を札幌で予定しています。多分9月の初旬頃になると思います。
東京(or 横浜)でも準備ができしだい開催したいと思います。

2014年7月3日木曜日

関数型プログラミング技術マップ2014

来週の月曜(7月7日)に札幌で「実務者のためのかんたんScalaz」という勉強会を予定しています。
この勉強会は、クラウドアプリケーション開発の基本技術の情報展開を目的としたもので、以下の勉強会をシリーズで行っていく予定です。
  • 実務者のためのかんたんScalaプログラミング
  • 実務者のためのかんたんScala入出力プログラミング
  • 実務者のためのかんたんScala設計
  • 実務者のためのかんたんオブジェクト指向分析/設計
クラウドアプリケーションの開発技法を整備する上では、業務アプリケーション開発の基盤技術であるオブジェクト指向分析/設計、オブジェクト指向プログラミングの土台の上に関数型言語をどうのように接合していくのか、という点が焦点の一つと思います。
この議論を深めるためには、関数型言語の技術的な背景、文脈を整理することが必要ということで2012年に「関数型言語の技術マップ」を作成しました。
今回、勉強会向けにこの技術マップを改良してみました。
改良点は以下の2つです。
  • 純粋関数型言語の性質
  • モナドと圏論の位置付け

純粋関数型言語の性質

純粋関数型言語の性質として以下のものを参考に補足しました。
  • 不変 (immutable)
  • 副作用なし (no side effect)
  • 参照透過性 (referential transparency)
  • 置換モデル (substitution model)

モナドと圏論の位置付け

圏論の入門書を幾つかチェックしましたが、モナドについてはほとんど記載がなく、モナドは圏論の基本概念ではないらしいということが分かりました。
また、"圏論の中に(基本構成要素として?)モナドがあるのは違和感がある"というコメントもいただき、技術マップの中で圏論やモナドをどのように位置付けるかが懸案事項になっていました。
その後、色々と調べてみて分かったのですが、圏論については、種々存在する数学や情報科学の各分野を統一的に記述するための「普遍言語」と考えるのが適切のようです。
代数的構造だけでなく、プログラミング言語の構造や論理学も圏論で記述することが可能という関係になります。図にはこの理解を反映しました。
たとえば、位相空間の圏であるトポスで数理論理学のモデルを記述することができるようです。またプログラミング言語の圏としてHaskellのHask圏が有名です。
その上で「モナド(monad)」ですが、これは「プログラミング言語の圏で有効な概念」でこれを情報科学的に記述する時に圏論を用いる、という関係と考えるとよさそうです。「プログラミング言語の圏で有効な概念」なので、圏論を知らなくてもプログラミングに便利に使える概念のはずです。
圏論もモナドも難解な概念であるため、クラウドアプリケーションの開発者がどこまで理解しておく必要があるのか、という点が重要な論点になります。
この2つの概念を黒帯レベルで理解することが必須ということになると、現場への展開は事実上不可能になってしまいます。とはいえ全く知らないで良いということでもないと思うので、その線引が非常に重要になってきます。
勉強会では、ボクがこの辺りがよいのではと思っているラインについて、Scala言語での具体的なアプローチ方法も含めて、私案を展開したいと思います。

2014年6月30日月曜日

実務者のためのかんたんScalaz

一昨年からEverforthに参画してScalaを使ってとあるシステム開発を行いつつ、クラウドアプリケーションの開発技法について整備してきましたが、ある程度方向性が見えてきたので、札幌の開発部隊に情報展開するための勉強会を企画しました。

クラウドアプリケーションの開発は、ちょうど業務アプリケーション開発にオブジェクト指向技術が導入された時と同じようなパラダイムシフトが起きることになると予想されます。

新しいパラダイムには、難解な理論的な支柱が背景に控えているわけですが、難解な理論を正確に理解することが業務アプリケーション開発を行う必要条件というわけではないのは今も昔も同じです。

難しい部分はフレームワークで吸収したり、イディオム(公式)として覚えてしまう、という方法で多くのケースは対応できるでしょう。

実際のビジネスを展開する上では、新しいパラダイムの上で実案件をこなすことができるエンジニアの層を厚くすることが極めて重要です。

今回は、新しいパラダイムの全体像をつかみやすくすることを目的として、あえてエッジの技術であるScalazをテーマにしてみました。

せっかくなので、Everforth関係者だけでなく、この分野に興味をお持ちの一般のエンジニアの方も参加可能な形にしたいと思います。

平日の昼間なのと告知期間も短く、内容もマニアックなので、あまり外部参加者は多くないと予想されます。

このため、ATNDのようなイベント開催ツールは使わないことにします。興味のある方はお気軽にお越しください。

勉強会の内容

タイトル:

実務者のためのかんたんScalaz

概要:

難しい理論は抜きにしてScalazを用いた、クラウドアプリケーション開発に対応した関数型プログラミングを行うテクニックを解説します。

合わせて、クラウドアプリケーション開発の新しいプログラミング・パラダイムの全体像についての情報共有を行います。

勉強会の構成:

  • 前半(1:30〜3:30): 講義
  • 後半(4:00〜7:00): ハンズオン

場所:

  • 13:30〜17:00 札幌エルプラザの環境研修室1 (開場 13:00)
  • 17:00〜19:00 札幌カフェ5F

場所が連続して確保できなかったので途中で移動が入ります。ちょうどハンズオンの途中になりますが、このタイミングで参加終了しても大丈夫です。逆にハンズオンのみの参加も歓迎です。

環境:

  • コンセント(不明)
  • 無線LAN(不明)

持参頂くもの:

ハンズオンでScalaプログラミングをしていただきますので、以下の環境を整えておいてください。セットアップは、当日他の参加者に手伝ってもらうことができますが、各種モジュールのダウンロードに時間がかかることが予想されるので、必要なモジュールのダウンロードはしておくとよいと思います。

  • PC
  • Scalaコンパイラ
  • sbt
  • Scalaプログラムを編集するエディタ

参加資格:

Everforthの開発に参加しているエンジニア向けですが、一般のエンジニアの参加も歓迎です。

当日会場に直接おいでください。

懇親会:

Scala, Scalaz, 関数型プログラミング、クラウドアプリケーションについての情報交換もかねて懇親会を予定しています。ご都合のよい方はぜひご参加ください。

今後の予定

今回のScalazに続いて以下の勉強会を予定しています。

  • 実務者のためのかんたんScalaプログラミング
  • 実務者のためのかんたんScala入出力プログラミング
  • 実務者のためのかんたんScala設計
  • 実務者のためのかんたんオブジェクト指向分析/設計

シリーズを通して、クラウドアプリケーション開発の土台となる新しいプログラミング・パラダイムの概要をオブジェクト指向分析/設計からのトップダウン、関数型プログラミングからのボトムアップの両面から解説していきます。

全体像をつかむのと同時に、実案件にすぐに入れる基本技術(イディオムなど)の習得も目的としています。

シリーズの全体テーマは、以下のページにあるOFADについての2012年頃の考察を、実システム開発で実践した上でのフィードバックをベースに内容を深化させたものを、実務者向けの切り口で整備したものになります。

2014年6月18日水曜日

Scala Tips/ライブラリ選択(2014年6月バージョン)

どのようなプログラミング言語を使っても、本格的な応用では基本ライブラリだけでニーズが満たせるということはなく、用途に応じて外部ライブラリを併用することになります。

Scalaプログラミングをする上で、ボク的に常に使用するライブラリというのがだいたい固まって来たのでまとめてみました。

scalaz

以下の理由で手放せないライブラリになっています。

  • 「かんたんScalaz」としてご紹介している各種の便利機能
  • 型クラスMonoidの存在
  • 純粋関数型データ構造Tree

また使用頻度は落ちますが、以下の機能も魅力的です。

  • 型クラスTraverse/Foldable
  • 純粋関数型データ構造EphemeralStream
  • Task/Future

最近scalaz-steramを使っていて、どうもTaskが便利らしい、ということが分かってきました。これから使用頻度が高くなるかもしれません。ScalaのFuture/Promiseとの使い分けが悩ましいですね。

scalaz-stream

大規模データに対する操作や、データ操作を並列処理をするための基盤として最近使い始めました。かなりよいです。

scalax.io

Scalaの基本ライブラリはI/O機能が弱いので、本ライブラリは重宝します。以下の2つを使っています。

  • scala-io-core
  • scala-io-file

scala-arm

色々批判もあるようですが、やっぱり便利です。

自作のクラスでもcloseメソッドやdisposeメソッドを定義すれば、自動的にクローズしてくれる機能をヘビーユースしています。

nscala-time

時間周りの処理はやはりJoda-Timeが便利です。そのScalaラッパーということでnscala-timeを使用しています。

dispatch-core

RESTアクセス用に使っています。

難しい記号を多用するのが弱点ですが、色々便利なのは確か。

scalatest

Specs2との二択ですが、ボクはScalaTestの方を使っています。

play-json

Jsonライブラリは、Play上で使っているplay-jsonをPlay外でも使うようになりました。

他にも便利そうなライブラリはありますが、play-jsonが案外高速のようなので無理をして乗り換えるほどではないかな、と今の所は考えています。

anorm & squeryl

DBアクセスはanormとsquerylを併用しています。

anormは、SqlRowが使いやすいのでこれが目当てです。

簡単にちょちょちょっと書きたい所はsquerylが便利。

ただ、この分野はSlickが使いやすければ乗り換えるかもしれません。

2014年6月13日金曜日

実用Scalaz/Option + Monoid

OptionはScalaプログラミングのキーパーツであり、Optionの捌き方の巧拙がScalaプログラミングの効率に直結します。

これと同様にMonoidはScalazプログラミングのキーパーツということができるかと思います。Monoidの捌き方の巧拙がScalazプログラミング、さらにはMonadicプログラミングの効率に直結することになるでしょう。

そして、ScalazのおいてOptionは代表的なMonoidです。つまり、OptionをMonoidとして捌いていく技法はScalazプログラミング、Monadicプログラミングにおいて最重要のテクニックといえるわけです。

というととても難しい技術のように見えますが、(背景の数学的な理論は別として)使い方はとても簡単で、さらに頻出のユースケースをカバーする非常に便利なイディオムです。

準備

説明の準備に以下の関数を定義します。

scala> def a: Option[Int] = Some(100)
def a: Option[Int] = Some(100)
a: Option[Int]

scala> def b: Option[Int] = None
def b: Option[Int] = None
b: Option[Int]

よくある処理

プログラムを書いているとよく出てくるのが以下のような処理です。

def plus(lhs: Option[Int], rhs: Option[Int]): Option[Int] = {
  lhsの中身とrhsの中身を加算する
  ただしlhsまたはrhsのどちらか一方がNoneの場合はSomeの方の値を使用する
  両方がNoneの場合はNoneにする
}

よくある間違い

この処理を書く場合、Optionを使ってMonadicに処理すると行けそうに思えます。そこで、次のような処理を書いたとしましょう。

def plus(lhs: Option[Int], rhs: Option[Int]): Option[Int] = {
d lhs.flatMap(x => rhs.map(y => x + y))
}

これは、for式を使って以下のように書くこともできます。

def plus(lhs: Option[Int], rhs: Option[Int]): Option[Int] = {
  for (x <- lhs; y <- rhs) yield x + y
}

さて、この結果ですが、以下のようになります。

scala> plus(a, a)
res1: Option[Int] = Some(200)

scala> plus(a, b)
res3: Option[Int] = None

scala> plus(b, a)
res4: Option[Int] = None

scala> plus(b, b)
res2: Option[Int] = None

Monadicに処理した場合は、残念ながらいずれかのOptionがNoneだった場合に、結果もNoneになってしまうわけです。このような処理が適切なケースも多いわけですが、前述の「plus関数」の定義とは異なるのでこの実装は使えません。

Match式

「plus関数」をmatch式を使って実装すると以下のようになります。

def plus(lhs: Option[Int], rhs: Option[Int]): Option[Int] = {
  (lhs, rhs) match {
    case (Some(l), Some(r)) => Some(l + r)
    case (Some(l), None) => Some(l)
    case (None, Some(r)) => Some(r)
    case (None, None) => None
  }
}

このようなOptionの組み合わせごとに処理を分けるmatch式はScalaプログラミングをしているとよく出てくるのではないでしょうか?しかも、かなり野暮ったい感じなのでこれを簡単に書けると好都合です。

Monoid

前出のmatch式による処理をScalazのMonoidで書くと以下になります。複雑なmatch式を演算子「|+|」のみで記述できています。

ScalazではOptionのMonoid演算として、前述のmatch式相当の演算を割り当てているわけですね。

def plus(lhs: Option[Int], rhs: Option[Int]): Option[Int] = {
  lhs |+| rhs
}

この「plus」関数の実行結果は以下になります。

scala> plus(a, a)
res9: Option[Int] = Some(200)

scala> plus(a, b)
res10: Option[Int] = Some(100)

scala> plus(b, a)
res11: Option[Int] = Some(100)

scala> plus(b, b)
res13: Option[Int] = None

おさらい

おさらいの意味でOptionに対してMonoidの演算子「|+|」を適用して演算を行った結果を以下に示します。

scala> 1.some |+| 2.some
1.some |+| 2.some
res40: Option[Int] = Some(3)

scala> 1.some |+| none[Int]
1.some |+| none[Int]
res54: Option[Int] = Some(1)

scala> none[Int] |+| 2.some
none[Int] |+| 2.some
res42: Option[Int] = Some(2)

scala> none[Int] |+| none[Int]
none[Int] |+| none[Int]
res53: Option[Int] = None

参考

Option Index

Optionに関する2012年ごろのブログのまとめです。

Monoid Index

Monoidに関する2012年ごろのブログのまとめです。

諸元

  • Scala 2.10.4
  • Scalaz 7.0.6

2014年6月2日月曜日

かんたんScalaz/Option編 SomeとNone

Optionを使う場合にSomeやNoneを作成する処理は当然ながら頻出です。

scala> val a = Some(5)
val a = Some(5)
a: Some[Int] = Some(5)

scala> val b = None
val b = None
b: None.type = None

通常はこれで問題ないのですが、若干扱いにくい点があります。

上記処理で得られる型にご注目下さい。

「Some(5)」を代入(束縛?)した変数aの型がSome[Int]になっています。さらに、Noneを代入した変数の方がNone.typeになっています。

「Some(5)」や「None」は、型「Option[Int]」の値として扱われる事が多いので、「Some(5)」や「None」を型「Option[Int]」として生成する手段があるとさらに便利になります。

たとえば以下のようなメソッド定義のケースです。この場合、メソッドの返却値の型の定義を省略することができます。

scala> def getFoo = Some(5)
def getFoo = Some(5)
getFoo: Some[Int]

scala> def getBar = None
def getBar = None
getBar: None.type

またよくあるのは以下のような畳み込み処理の初期値です。畳み込みの初期値の型が「Option[Int]」ではなく「Some[Int]」になっているため、式全体の演算結果としてNoneを返すことができなくなっています。

scala> Vector(1, 2, 3, 4, 5).foldLeft(Some(0))((z, x) => if (x % 2 == 0) None else z.map(_ + x))
se z.map(_ + x))
<console>:14: error: type mismatch;
 found   : None.type
 required: Some[Int]
              Vector(1, 2, 3, 4, 5).foldLeft(Some(0))((z, x) => if (x % 2 == 0) None else z.map(_ + x))
                                                                                ^

Scalaの解

まず、この問題に対するScalaでの対策です。

値がある場合はOption#applyメソッドを使用、値がない場合はOption#emptyメソッドに型「Int」を指定することで型「Option[Int]」を得ることができます。

scala> Option(5)
Option(5)
res14: Option[Int] = Some(5)

scala> Option.empty[Int]
Option.empty[Int]
res16: Option[Int] = None

前述の例をこの方法で書きなおしたものが以下になります。

scala> def getFoo = Option(5)
def getFoo = Option(5)
getFoo: Option[Int]

scala> def getBar = Option.empty[Int]
def getBar = Option.empty[Int]
getBar: Option[Int]

scala> Vector(1, 2, 3, 4, 5).foldLeft(Option(0))((z, x) => if (x % 2 == 0) None else z.map(_ + x))
else z.map(_ + x))
res17: Option[Int] = None

Scalazの解

Scalazではこの問題に対応するため、任意の値を型「Option[T]」のSome[T]またはNoneに持ち上げる機能を提供しています。

scala> 5.some
res30: Option[Int] = Some(5)

scala> none[Int]
res33: Option[Int] = None

前述の例をこの方法で書き直したものが以下になります。

scala> def getFoo = 5.some
getFoo: Option[Int]

scala> def getBar = none[Int]
getBar: Option[Int]

scala> Vector(1, 2, 3, 4, 5).foldLeft(0.some)((z, x) => if (x % 2 == 0) None else z.map(_ + x))
e z.map(_ + x))
res18: Option[Int] = None

個人的な好みの問題もありますがScala版に対して以下の優位点があると思います。

  • プログラミング時に書きやすい
  • 可読性が高い

具体的には「5.some」の記法は「Option(5)」の記法と比べて以下の優位点があると思います。

プログラミング時の書きやすさ

Scala版では、プログラミング時に「5」と書いた後にSome化したいと思った場合、一度カーソルを前に戻して「Some(」を書いた後、カーソルを文末に移動させて「)」を閉じるという作業が必要になります。それに対して、Scalaz版では「5」と書いた後にSome化したいと思った場合「.some」と書くだけですみます。プログラミングの流れを止めません。

またScala版では「Some(5)」が欲しい時に、場合によっては「Option(5)」にしないといけないので、毎回判断が必要になります。Scalaz版ではどの場合でも「5.some」でOKです。

可読性

Scala版では「Some(5)」の意味で「Option(5)」を使うことがありますが、Optionというオブジェクト名を使用するため、微差ですが意図がわかりづらい面があると思います。

別の切り口として、重要な情報が左側に来る方が可読性が高くなると思います。これを前提にすると、Scala版の「Some(5)」の場合「Some」であることが重要で、「5」の重要度はその次という印象になります。一方、Scalaz版の「5.some」の場合「5」の値が重要で、「some」の重要度はその次という印象になります。多くの場合、「5」の方が重要なので、(これまた微差ですが)「Some(5)」より「5.some」の方が可読性が優れているのではないかと思います。

Scalaz版のまとめ

「プログラミング時の書きやすさ」、「可読性」のいずれも微差なので、好みの方法を使えばよいわけですが、ボクはScalazの提供する「5.some」、「none[Int]」の記法を愛用しています。

参考

今回の記事は、基本的には2012年の以下の記事と同じ内容を再整理したものです。

棚卸しという意味で記事化しました。

諸元

  • Scala 2.10.4
  • Scalaz 7.0.6

2014年5月26日月曜日

かんたんScalaz/Option編

かんたんScalazのOption編です。

Scalaプログラミングでは、Optionはキーとなる重要なオブジェクトで、Optionの取り回し方の優劣がプログラミングの精度・効率に大きく作用します。

ScalazでもOption向けに多くの機能を用意しています。

「かんたんScalaz」ということでScalazが提供しているMonadやMonoidといった難しい概念を抜きにして便利に使えるOptionの機能をまとめてみました。

準備

説明の準備に以下の関数を定義します。

scala> def a: Option[Int] = Some(100)
def a: Option[Int] = Some(100)
a: Option[Int]

scala> def b: Option[Int] = None
def b: Option[Int] = None
b: Option[Int]

?と|

OptionをBoolean型に見立てて値を決めたい場合に、3項演算子的な書き方ができると便利です。Scalazでは「?」と「|」の組合せでこれが実現できます。

scala> a ? "defined" | "undefined"
a ? "defined" | "undefined"
res12: String = defined

scala> b ? "defined" | "undefined"
b ? "defined" | "undefined"
res13: String = undefined

Scala基本機能のみでもif式で以下のように書ける上に実行性能も圧倒的にこちらの方が速いので、通常はこれでも十分ですが、上記の方が短く書けるので好みによって使ってみるのもよいと思います。

scala> if (a.isDefined) "defined" else "undefined"
if (a.isDefined) "defined" else "undefined"
res44: String = defined

個人的には「?」と「|」の方が可読性が高いように思うので、性能が気にならないところ(例:フレームワークのコア機能以外)ではよく使っています。

OptionがSomeだった場合は格納された値、そうでなかった場合はデフォルト値を取得する処理はよく出てきます。

通常は、OptionのgetOrElseメソッドを使いますが、Scalazでは「|」でこれを記述することができます。

scala> a | 10
a | 10
res17: Int = 100

scala> b | 10
b | 10
res18: Int = 10

個人的には「|」の方が可読性が高いように思うので、性能が気にならないところ(例:フレームワークのコア機能以外)ではよく使っています。

some/none

OptionがSomeだった場合は格納された値に演算を施した値、そうでなかった場合はデフォルト値を取得する処理もよく出てきます。

Scalaの基本機能で記述する場合、以下のようになると思います。

scala> a match {
  case Some(x) => x + 100
  case None => 0
}
res45: Int = 200

scala> a.map(_ + 100).getOrElse(0)
a.map(_ + 100).getOrElse(0)
res46: Int = 200

どちらの方法でもよいですが、頻出処理なのでもっと簡略化した記法が使えるとうれしいところです。

Scalazではこの目的で「some」と「none」の組合せでの記述方法を用意しています。

scala> a some(_ + 100) none(0)
a some(_ + 100) none(0)
res15: Int = 200

scala> b some(_ + 100) none(0)
b some(_ + 100) none(0)
res16: Int = 0

また前述の「|」を使用した以下の記述方法も便利です。

scala> a.map(_ + 100) | 0
a.map(_ + 100) | 0
res47: Int = 200

orZero

orZeroメソッドは「かんたんScalaz/Boolean編」で紹介した「??」メソッドや「!?」メソッドで用いているMonoidの性質を利用したメソッドです。

OptionがSomeの場合は格納された値を返しますが、そうでない場合は格納された値のMonoidとしての単位元を返します。このためMonoidである型にしか適用できませんが、基本データ型やコレクションなどはほとんどMonoidなのでかなり適用範囲は広いです。

scala> a.orZero
a.orZero
res22: Int = 100

scala> b.orZero
b.orZero
res23: Int = 0

上記プログラムの動きは以下のようになります。

OptionがSomeの場合は、Someに格納されている値である100が返ります。一方Noneの場合は、以下の動きになっています。

  • Optionに格納されている型はInt
  • Intの単位元は「0」
  • OptionがNoneなのでIntの単位元である「0」を返す

Monoidというと敷居が高いので、「かんたんScalaz」の文脈では型ごとに初期値を持っている、ぐらいの捉え方でよいと思います。この「初期値」は、数値系なら「0」、ListなどのコレクションはNilなどの「空」となりますので、0や空といった値になると覚えておいて、実際の値は必要に応じて調べるというアプローチでよいでしょう。

「〜」を使う以下の書き方も用意されていますが、見落としや誤読してしまいそうなのでボクは使わないようにしています。

scala> ~a
res48: Int = 100

scala> ~b
res49: Int = 0

参考

Option Index

Optionに関する2012年ごろのブログのまとめです。

Optionに関しては、この当時とそれほど見方は変わっていません。このため、このあたりの記事も現役として参考にしていただけると思います。

ただ、プログラミングしている中で色々なバランスが見えてきた部分もあると思うので、棚卸しをした上で適宜追加情報や新しいバランス上での使い方についてまとめていきたいと思います。

諸元

  • Scala 2.10.4
  • Scalaz 7.0.6

2014年5月19日月曜日

かんたんScalaz/Booean論理記号

ScalazではBooleanに以下の論理演算子を追加しています。







演算子意味別名Scala
Conjunction, AND/\&&
Disjunction, OR\/||
!||Negation of Disjunction, NOR
!&&Negation of Conjunction, NAND
ーー>Conditional
<ーーInverse Conditional
Negational of Conditionalー/>
Negation of Inverse Conditional<\ー

かんたんScalazの観点では、以下のような扱いがよいと思います。

  • 「ならば」の論理演算である「ーー>」は覚える価値がある。
  • 他の演算子は必要に応じて。

ーー>

Scala(や通常のプログラミング言語)が提供していない論理演算子の中で「ならば(Conditional, 論理包含、条件文)」を表す論理演算子「ーー>」はなかなか有用ではないかと思います。「ーー>」の使い所はたとえば以下のようなassertです。

assert (!isCacheable(x) --> !isCached(x), "キャッシュ可能でない場合、キャッシュにデータが入っているのはおかしい。")

このケースだと、キャッシュ可能の場合はキャッシュにデータがあってもなくてもよいので、「ならば」の論理演算の条件にぴったり合います。

ちなみに、上記のassertは「<ーー」(Inverse Conditional, 逆論理包含?)を使って以下のようにも書けるようです。

assert (isCacheable(x) <-- isCached(x), "キャッシュにデータが入っている場合、キャッシュ可能である必要がある。")

∧と∨

論理積の演算子に「∧」や「/\」、論理和の演算子に「∨」や「\/」が定義されていますが、Scalaの論理演算子「&&」、「||」とかぶるので使用しても通常のプログラミングが特に便利になるというものではありません。逆に実行時のオーバーヘッドは確実にあるので、「∧」や「∨」といった数学記号をどうしても使いたい局面でなければ無理をして使う感じでもないでしょう。

否定形

以下のものは他の演算子の否定形です。否定形があると覚えておいて、使う時に仕様を確認する形でよいでしょう。





論理記号別名否定元
!||||
!&&&&
ー/>ーー>
<\ー<ーー

諸元

  • Scala 2.10.4
  • Scalaz 7.0.6

2014年5月12日月曜日

かんたんScalaz/Boolean編

Scalaz 7を本格的に使い始めたので、Scalaz 7を包含したコーディング・イディオムの棚卸しをしています。Scala 2.10の基本機能とScalaz 7を併用する前提で、コーディングの局面毎に使用するイディオムを事前に準備しておくわけです。イディオムを事前準備しておくことで、プログラミングの局面局面で即断即決ができるので効率よくプログラミングを進めることができます。またイディオムは利便性や実行速度などの要因を考慮に入れて使い所を絞り込んでおくので、プログラムの品質や性能などの向上も期待できます。

Scalazは、MonadやMonoidといった代数系の概念を軸にした型クラスやや純粋関数型データ構造を提供するライブラリですが、小回りの効いた便利機能も数多く提供しています。これらの便利機能は特にMonadやMonoidといった概念を抜きにして、便利につかえるので「かんたんScalaz」としてまとめていきたいと思います。

今回はBoolean向けの便利機能です。

準備

説明の準備に以下の関数を定義します。

scala> def t: Boolean = true
def t: Boolean = true
t: Boolean

scala> def f: Boolean = false
def f: Boolean = false
f: Boolean

option

ScalazではBooleanにoptionメソッドを追加しています。optionメソッドは、Booleanがtrueであれば指定された値を返すSomeを、falseであればNoneを返します。

使い方は以下になります。

scala> t option 100
t option 100
res3: Option[Int] = Some(100)

scala> f option 100
f option 100
res4: Option[Int] = None

これをScalaネイティブの文法で書くとif式を用いた以下になります。これでも問題ないといえばないのと実効速度は確実に速いので、Scalazのoptionメソッドは無理をして使う必要はありませんが、プログラミング時にはかゆいところに手が届くという感じで便利なんですよね。

if (t) Option(100) else None

たとえば、次のような感じでBooleanがtrueの時の処理を長めに書く時に重宝します。

t option {
  val a = somework
  val b = somework(a)
  somework(b)
}

??と!?

ScalazではMonoidという型クラスを提供していますが、これがすこぶる便利なんです。

その便利さの一つが単位元です。Monoidの性質を持つオブジェクトは、単位元という特別なインスタンスが定義されます。この単位元をデフォルト値や初期値として用いることで、プログラミングを簡略化するテクニックがありますが、この実例の一つがBooleanの「??」メソッドと「!?」メソッドです。

「??」メソッドは、Booleanがtrueであれば指定された値を、falseの場合は型の単位元を返します。

具体的には以下のような動きになります。

scala> t ?? 100
t ?? 100
res5: Int = 100

scala> f ?? 100
f ?? 100
res6: Int = 0

Booleanの値がtrueの場合は、指定された値である100が返ります。一方falseの場合は、以下の動きになっています。

  • 指定された値「100」の型はInt
  • この式全体の型はInt
  • Intの単位元は「0」
  • Booleanの値がfalseなのでIntの単位元である「0」を返す

このようにMonoidの性質とScalaの型推論のコンビネーションで、非常にコンパクトに目的の式を記述することができるわけです。

Monoidというと敷居が高いので、「かんたんScalaz」の文脈では型ごとに初期値を持っている、ぐらいの捉え方でよいと思います。この「初期値」は、数値系なら「0」、ListなどはNilとなりますので、0や空といった値になると覚えておいて、実際の値は必要に応じて調べるというアプローチでよいでしょう。

?!

「??」メソッドの否定形は「?!」メソッドになります。

scala> t !? 100
t !? 100
res165: Int = 0

scala> f !? 100
f !? 100
res167: Int = 100

? |

Javaで提供されていた3項演算子は、Scalaには取り込まれずif式を使用するという建付けになっています。

scala> if (t) 100 else 200
if (t) 100 else 200
res0: Int = 100

Scalazでは「?」メソッドと「|」メソッドのコンビネーションで3項演算子っぽく記述することが可能です。

scala> t ? 100 | 200
t ? 100 | 200
res1: Int = 100

scala> f ? 100 | 200
f ? 100 | 200
res2: Int = 200

「?」メソッド&「|」メソッドとは別にfoldメソッドというのも用意されていますが、こちらは特段便利になったような感じはしません。if式と比べると実行速度は確実に落ちるので、foldを使うのならif式を使う方がよさそうです。

scala> t fold (100, 200)
t fold (100, 200)
res9: Int = 100

scala> f fold (100, 200)
f fold (100, 200)
res10: Int = 200

「?」メソッド&「|」メソッドもif式に比べると実行速度は確実に落ちるので無理をして使うほどのことはありませんが、短くかけるとプログラミングが楽になる局面(式を1行に収めたい等)はあるので、コーディング・イディオムに加えておくのもよいと思います。

参考

Boolean

2012年のブログです。

Booleanについてはあまり認識は変わっていないようです。

ただ、2012年当時とは色々とアプローチが変わった部分もあるので、技術の棚卸しという意味で過去のブログで取り上げた内容も再度取り上げていく予定です。

Monoid

Monoidに関する2012年ごろのブログのまとめです。

Scalazが提供する代数概念の型クラスのうち、MonadはScalaネイティブでも基本機能は持っていますし、Aplicativeはfor式で代替することが可能です。

しかし、Monoidだけは該当する機能がありません。このMonoidが非常に便利なので、これを使うためにScalazを使うという面が大きいです。

諸元

  • Scala 2.10.4
  • Scalaz 7.0.6

2014年5月7日水曜日

Scala 2.10

つい先日Scala 2.11がリリースされたばかりですが、タイトルの「Scala 2.10」は間違いではありません。

一昨年の後半からとあるシステム開発に注力していたため時間が取れずブログ更新を中断していましたが、ようやく一段落してきたので、自分向けに技術情報の棚卸しをしていくという意味でもぼちぼち書いていきたいと思います。

最近うれしかったことの一つはようやく製品開発にScala 2.10+Scalaz 7を使えるようになったことです。システムは一度動き始めると、なかなか使用しているコンパイラ、ライブラリ、ミドルウェアのバージョンを上げることはできません。特にバージョン間での互換性が高くないことで有名(?)なScalaの場合はなおさらですが、Akkaを本格的に使おうとするとさすがにそうもいっておられず、大量のコンパイルエラーと警告を克服してやっとScala 2.10+Scalaz 7に上げることができたしだいです。

ということで、(いまさらですが)Scala 2.10を本格的に使ってみた感想です。

  • warningの強化
  • Try
  • Future
  • Implicit Class/Value Class
  • feature機能
  • String Interpolation

warningの強化

Scala 2.10で、予想外にうれしかったことが「==」の型チェック機能の強化です。この機能のお陰で移植時に復数のバグを見つけることができました。

scala> val x = Some("5")
val x = Some("5")
x: Some[String] = Some(5)

scala> val y = "5"
val y = "5"
y: String = 5

scala> println(x == y)
println(x == y)
<console>:16: warning: comparing values of types Some[String] and String using `==' will always yield false
              println(x == y)
                        ^
false

ただ、改めて試してみると:

def f(a: String, oa: Option[String]): Boolean = a == oa

といったケースでは警告はでないようです。

また:

List(1, 2, 3).contains("5")

といったcontainsメソッドの型チェック問題は依然としてオープンです。

==メソッドやcontainsメソッドにおける型安全な等価性は、Scalaプログラミングで非常に重要なプログラミングテクニックだと思うので、いずれ取り上げてみたいと思います。

Try

Tryの導入によって2.10でプログラミングスタイルが大きく変わることは想定済みだったので、TryとNonFatalは2.9にバックポートして使っていました。このため、特に大きな問題はなく移行することができました。

想定外だったのが、ScalazでTryをMonadとして扱うことができない点です。例外が発生した時のTryの振舞いがモナド則を満たさないという問題があるようで、それが原因でScalaz側でTryの各種型クラスは提供していないのかなと思います。

ScalazでTryをサポートしていないため、Tryを使ってMonadicプログラミングすることに限界が出てきます。この辺りの事情も織り込んだ上で、例外をハンドリングする作戦を考えていく予定です。

Future

FutureはAkka Futureをほぼimport先を切り替えるだけで移植できました。

想定外だったのが、Tryと同様にScalazでMonadとして扱えない点です。Tryと同様に例外は発生した時の振舞いがモナド則を満たさないのが原因ではないかと推測します。

Scalazは、独自にFutureを用意しているので、MonadicプログラミングをするためにはこちらのFutureを使う事になりそうですが、本家Futureと併用することになるのでできれば避けたいところです。

Implicit Class/Value Class

Implicit Class/Value Classは、自分で直接使うことは今の所ほぼありません。ただ、型クラス機能を使用する際のオーバーヘッドが大幅に減少したはずなので、Scalazのような型クラスを多用するライブラリを安心して使えるようになりました。心理的な効果が非常に大きいです。

feature機能

implicit conversionはScalaの魅力的な機能の一つですが、バグが出た時のデバッグがとても大変になるので、ボクのコーディング戦略ではほぼ使わない機能になっています。具体的には、いわゆる「Pimp My Libraryパターン」で既存クラスを文脈依存で拡張したり、DSLの枠組み上必要な場合、以外は使わない機能です。

Scala 2.10からfeature機能が入って、拡張機能はコンパイルのパラメタかプログラム内で指定しないと警告が出るようになりました。上述のimplicit conversionもこの拡張機能に入っているので警告対象になりました。無自覚に使っている場合も警告によって存在を認識することができ重宝しました。

このimplicit conversion以外にも、今回のScala 2.10化では以下の警告が出ました。

  • postfixOps
  • existentials

postfixOpsはDSL的には残念ですが、非推奨の文法になったということなので、ソースコードを修正して対応しました。

プログラミング言語は互換性も大事ですが、Scalaのような伸び盛りの言語は新しい応用を取り込んでいくチャレンジも重要なので、feature機能とdeprecation機能によって時代遅れになってしまった機能、一般の開発者は使わない方がよい機能をうまく制御していくアプローチは非常によいと思いました。

String Interpolation

String Interpolationは:

  • s Interplatorやf Interpolatorの機能
  • String Interpolationの外部追加Iterpolatorへの期待
  • String InterpolationのInterpolator機能の開発

といった所が切り口になりますが、今の所はs Interplatorがそれなりに便利かな、という利用レベルです。

以前はformatメソッドを使っていましたが、引数の数を間違えると以下のようなランタイムエラーが出るのが問題点としてありました。この問題が起きなくなるのが、体感的に感じたs Interplator導入の直接のメリットでした。もちろん、s Interplatorの方が若干性能向上も期待できるのは重要です。

> "%s = %s".format("1 + 1")
java.util.MissingFormatArgumentException: Format specifier 's'
...

SlickのSQL Interpolationといった応用が沢山出てくると色々と面白くなってきそうです。

Scala 2.11

Scala 2.11については、とりあえずはPlayの対応待ちというところです。

Scala 2.11はTuple /Case classの22個制限が外れるのが大きな魅力ですが、それ以外は特別に使ってみたい機能はないようなので、実際に使いはじめるのはまた1年後ぐらいということになるかもしれません。