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