2012年6月25日月曜日

Scala Tips / Reducer

ScalazではIntなどの整数値のモノイド演算は加算として定義しています。数値に対するモノイド演算で最も利用されそうなのが加算なので妥当な選択ですが、場合によっては乗算などの演算を数値のモノイド演算として使用したい場合もあります。

そこでScalazでは、乗算をモノイド演算として定義したMultiplicationというオブジェクトを提供しています。Multiplicationは以下の7種類が用意されています。

数値Multiplication
ByteByteMultiplication
CharCharMultiplication
ShortShortMultiplication
IntIntMultiplication
LongLongMultiplication
BigIntBigIntMultiplication
BigIntegerBigIntegerMultiplication

このように、あるクラスに対するモノイド演算が複数存在する場合には、「追加のモノイド演算」と「あるクラス」を結びつける「別のクラス」を作成し、この「別のクラス」に「追加のモノイド演算」を定義します。具体的には「別のクラス」と「追加のモノイド演算」を定義した型クラスMonoidインスタンスを定義します。

このように「あるクラス」に対する別モノイド演算を定義した「別のクラス」が併存するシーンでのプログラミングの共通処理を担うためのツールとして利用できそうなのがReducerです。Reducerは、「あるクラス」と「あるクラス」に対する別モノイド演算を定義した「別のクラス」を結びつける演算を行うオブジェクトです。今回は、このReducerについてみていきます。

以下では、元のクラスである「あるクラス」をC、「追加のモノイド演算」と「あるクラス」を結びつける「別のクラス」をMと表記することにします。

動作確認

まず、IntとIntMultiplicationを結びつけるIntProductReducerです。前述の表記法による場合は、IntがC、IntProductReducerがMです。

IntProductReducerは「Scalaz._」で取り込まれているので以下のように取り出して使用することができます。

scala> val r = IntProductReducer
r: scalaz.Reducer[Int,scalaz.IntMultiplication] = scalaz.Reducers$$anon$1@8dbe4e5

Reducerは以下の3つのメソッドを持っています。

unit
CからMを生成。
cons
CをMの左側から連結したMを返す。
snoc
CをMの右側から連結したMを返す。

モノイド演算では、2つの要素AとBを、ABの順で演算するのか、BAの演算するのかで演算結果が異なる可能性があります。(演算結果が異ならないものは特別に可換モノイドと呼びます。)

そこで、Reducerでは元のオブジェクトMに対して新しいオブジェクトCを左側からモノイド演算、すなわち「C+M」するためのメソッドとしてconsを提供しています。メソッド名consの由来は言うまでもなくLispのcons関数です。

また、元のオブジェクトMに対して新しいオブジェクトCを右側からモノイド演算、すなわち「M+C」するためのメソッドとしてsnocを提供しています。メソッド名snocは言うまでもなく「cons」を逆にしたものですね。

IntReducerのメソッドはそれぞれ以下の動きになります。

unit
IntからIntMultiplicationを生成。
cons
IntをIntMultipilcationの左側から連結したMを返す。
snoc
IntをIntMultiplicationの右側から連結したMを返す。

それぞれの動きは以下になります。

scala> r.unit(3)
res5: scalaz.IntMultiplication = 3

scala> r.cons(2, r.unit(3))
res2: scalaz.IntMultiplication = 6

scala> r.snoc(r.unit(3), 2)
res4: scalaz.IntMultiplication = 6

IntMultiplicationは可換モノイドなので、左側から足しても右側から足しても結果は変わりません。このため、consメソッドの場合も、snocメソッドの場合も結果は「10」になります。

IntMultiplicationを直接使う

上記の処理をIntMultiplicationを直接使って記述すると以下になります。

scala> multiplication(3)
res6: scalaz.IntMultiplication = 3

scala> multiplication(2) |+| multiplication(3)
res7: scalaz.IntMultiplication = 6

scala> multiplication(3) |+| multiplication(2)
res8: scalaz.IntMultiplication = 6

ノート

関数型プログラミングは、どうも「モノイド」が重要概念の一つで、プログラミングテクニックとしてあちこちに登場してきます。

Scalazでは、型クラス「Monoid」を提供しておりモノイド演算を行う演算子「|@|」を導入しています。演算子「|@|」を使って処理を記述することで、任意のモノイドに対する共通関数が記述できることをブログ内でも度々取り上げてきました。

問題は一つのクラスに対して複数のモノイド演算を定義する場合です。Intに対するIntMultiplicationといったように複数のクラスでモノイド演算を行う場合に、共通処理を記述するためのメカニズムの一つとしてReducerが導入されていると考えられます。

実際に所Reducerを使うシーンは少なそうですが、モノイドを操作するテクニックを引き出しに入れておくと色々応用が効きそうです。そういう意味でReducerは面白い素材なのでもう少し見ていく予定です。

諸元

  • Scala 2.9.2
  • Scalaz 6.0.4

0 件のコメント:

コメントを投稿