クラウド温泉3.0@小樽のセッション「Monadicプログラミング・マニアックス」で使用するスライドのネタ検討その8です。
前回は関数を用いたMonadicプログラミングについて考えました。今回はFunctorを用いたMonadicプログラミングについて考えます。
前回、パイプライン・プログラミングを広義のMonadicプログラミングと定義しました。
今回は、FunctorによるMonadicプログラミングを考えます。FunctorはMonadではありませんが、パイプライン演算に見立てることができます。
mapメソッド
Listなどのコレクションクラスが提供するmapメソッドはコンテナに格納されているオブジェクトに対して計算した結果得られた新たなオブジェクトに詰めなおしたコンテナを生成します。このようなmapメソッドを持つオブジェクトを一般的にFunctorと呼びます。ScalaとFunctorの関係は「Scala で圏論入門」に詳しいです。
mapメソッドの使い方は以下になります。
scala> List(1, 2, 3).map(x => x * 3) res1: List[Int] = List(3, 6, 9)
mapメソッドをつなぐことでパイプライン処理を記述することができます。
scala> List(1, 2, 3).map(x => x * 3).map(x => x + 5) res2: List[Int] = List(8, 11, 14)
Scalaでは関数リテラルでの省略記法が提供されているので、これを用いると以下のように書くことができます。
scala> List(1, 2, 3).map(_ * 3).map(_ + 5) res3: List[Int] = List(8, 11, 14)
関数
mapメソッドに直接関数リテラルで処理を記述してもよいのですが、再利用可能な部品として関数を用意しておいて、これを指定する使い方がより望ましいでしょう。
関数mul3とplus5を用意します。いずれも引数の数が1つの関数です。
def mul3(a: Int) = a * 3 def plus5(a: Int) = a + 5
これをmapメソッドで使うと以下になります。
scala> List(1, 2, 3).map(x => mul3(x)).map(x => plus5(x)) res11: List[Int] = List(8, 11, 14)
Scalaでは関数リテラルでの省略記法が提供されているので、これを用いると以下のように書くことができます。 こうなるとパイプラインに部品を接続することで処理を組み立てていることが明確になります。
scala> List(1, 2, 3).map(mul3).map(plus5) res4: List[Int] = List(8, 11, 14)
部分適用
引数の数が2つ以上の関数では部分適用を用いることで、mapによるパイプラインに適用できます。
関数の引数が複数あるケースを考えます。関数mulとplusを用意します。いずれも引数の数が2つの関数です。
def mul(a: Int, b: Int) = a * b def plus(a: Int, b: Int) = a + b
これをmapメソッドに適用すると以下になります。
scala> List(1, 2, 3).map(mul(3, _)).map(plus(5, _)) res5: List[Int] = List(8, 11, 14)
カリー化
関数がパイプラインの中で使われることが明らかな場合はカリー化しておくと便利です。カリー化した関数pluscとmulcは以下になります。
def mulc(a: Int)(b: Int) = a * b def plusc(a: Int)(b: Int) = a + b
pluscとmulcを用いてパイプラインを構築すると以下になります。
scala> List(1, 2, 3).map(mulc(3)).map(plusc(5)) res6: List[Int] = List(8, 11, 14)
ノート
広義のMonadicプログラミングをパイプライン・プログラミングと定義し、関数によるパイプライン・プログラミング、Functorによるパイプライン・プログラミングについてみてきました。
他に幾つかパイプライン・プログラミングの候補があるので、次回以降に取り上げる予定です。
スライドでは、パイプライン・プログラミングの実現技術をリストアップした後、これを一つにまとめてプログラミング・スタイルとして整備したいと考えています。
0 件のコメント:
コメントを投稿