More Related Content Similar to Monadicプログラミング マニアックス Similar to Monadicプログラミング マニアックス (20) More from Tomoharu ASAMI (20) Monadicプログラミング マニアックス3. 関連サイト
• Modegramming Style (テキストDSL駆動開発を
テーマにしたブログ)
• http://modegramming.blogspot.com/
• SimpleModeler
• http://github.com/asami/simplemodeler/
• SmartDox
• http://github.com/asami/smartdox
• g3フレームワーク
• http://code.google.com/p/goldenport3/
• g4フレームワーク
• http://github.com/asami/goldenport-android-
library/
7. 諸元
• スライド
• Scala 2.9.2
• Scalaz 6.0.4
• 最新
• Scala 2.10.0 M6
• Scalaz 7 開発中 (2012年年4⽉月リリース予定?)
• Scala 2.10で並列列プログラミング周りが⼤大幅に変更更され
る予定
• Akka(Actor)、STM、Future/Promise
• Scala 2.10 + Scalaz7で新しいバランスになるので、⾒見見極
めが必要
9. アプリケーションの階層と役割
アプリケー • DSLの作法に従ってビジネスロ
ジックを記述
ション • OO、関数型のスキルは最低限
• フレームワークを簡単に使⽤用する
DSL ための専⽤用⾔言語
• OO、関数型の⾼高度度なスキル
フレーム • ドメインの共通処理理を記述
ワーク • OO、関数型の⾼高度度なスキル
10. ⽤用語
• セッション内でのローカルな⽤用法
• (普通の)オブジェクト指向⾔言語
• Java的なオブジェクト指向⾔言語。オブジェクト+クラス+
抽象データ型+静的型付け+⼿手続き+値。
• 本物のオブジェクト指向⾔言語
• オブジェクトとメッセージから構成される核に何かを加え
たもの。
• 関数型⾔言語
• 純粋関数型⾔言語(での利利⽤用も可能)
• イミュータブル、参照透過性
• モナドが⼀一級市⺠民
• 型クラス(相当)
12. Monadicプログラミングで
やりたいこと
• 定義
• 普通
• Monadを使ったプログラミング。
• 本セッションでの広い意味
• Monadを中⼼心としつつも、旧来からあるFunctorによるプログ
ラミング、関数合成、コンビネータなども包含しつつ、パイプ
ライン・プログラミングのスタイルとしてまとめたもの。
• やりたいこと
• 楽々プログラミング
• 並列列プログラミング
• DSL
13. 楽々プログラミング
def main(args: Array[String]) {
records |> build部⾨門 >>> buildTree >>> showTree
}
def build部⾨門(records: Seq[Map[Symbol, Any]]): Map[Int, 部⾨門] = {
records.foldRight(Map[Int, 部⾨門]())((x, a) => {
val 部⾨門ID = x('部⾨門ID).asInstanceOf[Int]
val 部⾨門名 = x('部⾨門名).asInstanceOf[String]
val 親部⾨門ID = x('親部⾨門ID).asInstanceOf[Option[Int]]
val 親部⾨門名 = x.get('親部⾨門名).asInstanceOf[Option[String]]
a + (部⾨門ID -> 部⾨門(部⾨門ID, 部⾨門名, 親部⾨門ID, 親部⾨門名))
})
}
def buildTree(sections: Map[Int, 部⾨門]): Tree[部⾨門] = {
def build(sec: 部⾨門): Tree[部⾨門] = {
val children = sections collect {
case (k, v) if v.親部⾨門ID == sec.部⾨門ID.some => v
}
node(sec, children.toStream.sortBy(_.部⾨門ID).map(build))
}
build(sections(1))
}
def showTree(tree: Tree[部⾨門]) {
println(tree.drawTree(showA[部⾨門]))
}
14. val records = List(Map('部⾨門ID -> 1,
'部⾨門名 -> "営業統括", Map('部⾨門ID -> 121,
'親部⾨門ID -> None, '部⾨門名 -> "近畿⽀支店",
'親部⾨門名 -> None), '親部⾨門ID -> Some(12),
Map('部⾨門ID -> 11, '親部⾨門名 -> "⻄西⽇日本統括"),
'部⾨門名 -> "東⽇日本統括", Map('部⾨門ID -> 122,
'親部⾨門ID -> Some(1), '部⾨門名 -> "中国⽀支店",
'親部⾨門名 -> "営業統括"), '親部⾨門ID -> Some(12),
Map('部⾨門ID -> 12, '親部⾨門名 -> "⻄西⽇日本統括"),
'部⾨門名 -> "⻄西⽇日本統括", Map('部⾨門ID -> 123,
'親部⾨門ID -> Some(1), '部⾨門名 -> "四国⽀支店",
'親部⾨門名 -> "営業統括"), '親部⾨門ID -> Some(12),
Map('部⾨門ID -> 13, '親部⾨門名 -> "⻄西⽇日本統括"),
'部⾨門名 -> "⾸首都圏統括", Map('部⾨門ID -> 124,
'親部⾨門ID -> Some(1), '部⾨門名 -> "九州⽀支店",
'親部⾨門名 -> "営業統括"), '親部⾨門ID -> Some(12),
Map('部⾨門ID -> 111, '親部⾨門名 -> "⻄西⽇日本統括"),
'部⾨門名 -> "北北海道⽀支店", Map('部⾨門ID -> 125,
'親部⾨門ID -> Some(11), '部⾨門名 -> "沖縄⽀支店",
'親部⾨門名 -> "東⽇日本統括"), '親部⾨門ID -> Some(12),
Map('部⾨門ID -> 112, '親部⾨門名 -> "⻄西⽇日本統括"),
'部⾨門名 -> "東北北⽀支店", Map('部⾨門ID -> 131,
'親部⾨門ID -> Some(11), '部⾨門名 -> "東京⽀支店",
'親部⾨門名 -> "東⽇日本統括"), '親部⾨門ID -> Some(13),
Map('部⾨門ID -> 113, '親部⾨門名 -> "⾸首都圏統括"),
'部⾨門名 -> "北北陸陸⽀支店", Map('部⾨門ID -> 132,
'親部⾨門ID -> Some(11), '部⾨門名 -> "北北関東⽀支店",
'親部⾨門名 -> "東⽇日本統括"), '親部⾨門ID -> Some(13),
Map('部⾨門ID -> 114, '親部⾨門名 -> "⾸首都圏統括"),
'部⾨門名 -> "中部⽀支店", Map('部⾨門ID -> 133,
'親部⾨門ID -> Some(11), '部⾨門名 -> "南関東⽀支店",
'親部⾨門名 -> "東⽇日本統括"), '親部⾨門ID -> Some(13),
'親部⾨門名 -> "⾸首都圏統括"))
15. 部⾨門(1,営業統括,None,Some(None))
|
+- 部⾨門(11,東⽇日本統括,Some(1),Some(営業統括))
| |
| +- 部⾨門(111,北北海道⽀支店,Some(11),Some(東⽇日本統括))
| |
| +- 部⾨門(112,東北北⽀支店,Some(11),Some(東⽇日本統括))
| |
| +- 部⾨門(113,北北陸陸⽀支店,Some(11),Some(東⽇日本統括))
| |
| `- 部⾨門(114,中部⽀支店,Some(11),Some(東⽇日本統括))
|
+- 部⾨門(12,⻄西⽇日本統括,Some(1),Some(営業統括))
| |
| +- 部⾨門(121,近畿⽀支店,Some(12),Some(⻄西⽇日本統括))
| |
| +- 部⾨門(122,中国⽀支店,Some(12),Some(⻄西⽇日本統括))
| |
| +- 部⾨門(123,四国⽀支店,Some(12),Some(⻄西⽇日本統括))
| |
| +- 部⾨門(124,九州⽀支店,Some(12),Some(⻄西⽇日本統括))
| |
| `- 部⾨門(125,沖縄⽀支店,Some(12),Some(⻄西⽇日本統括))
|
`- 部⾨門(13,⾸首都圏統括,Some(1),Some(営業統括))
|
+- 部⾨門(131,東京⽀支店,Some(13),Some(⾸首都圏統括))
|
+- 部⾨門(132,北北関東⽀支店,Some(13),Some(⾸首都圏統括))
|
`- 部⾨門(133,南関東⽀支店,Some(13),Some(⾸首都圏統括))
16. 並列列プログラミング
準備
時間を測る関数 テスト関数
def go[T](a: => T): (T, Long) = {
val start = System.currentTimeMillis val f = (x: Int) => {
val r = a Thread.sleep(x * 100)
val end = System.currentTimeMillis x
(r, end - start) }
}
scala> go(f(10)) scala> go {
res176: (Int, Long) = (10,1000) | f(10)
|}
res253: (Int, Long) = (10,1001)
17. PromiseのKleisli
scala> val fp = f.promise
fp: scalaz.Kleisli[scalaz.concurrent.Promise,Int,Int] =
scalaz.Kleislis$$anon$1@9edaab8
Applicative
scala> go((fp(1) |@| fp(2) |@| fp(3))(_ + _ + _).get)
res215: (Int, Long) = (6,302)
scala> go(f(1) + f(2) + f(3))
res212: (Int, Long) = (6,603)
scala> go((1.some |@| 2.some |@| 3.some)(_ + _ + _).get)
res237: (Int, Long) = (6,1)
18. scala> go(List(1, 2, 3).map(fp).map(_.flatMap(fp)).sequence.get)
res220: (List[Int], Long) = (List(1, 2, 3),602)
scala> go(List(1, 2, 3).map(f).map(f))
res221: (List[Int], Long) = (List(1, 2, 3),1205)
テスト関数
val fx = (x: Int) => {
val t = math.abs(x - 4)
Thread.sleep(t * 100)
x
}
val fxp = fx.promise
19. scala> go(List(1, 2, 3).map(fp).map(_.flatMap(fxp)).sequence.get)
res222: (List[Int], Long) = (List(1, 2, 3),402)
scala> go(List(1, 2, 3).map(f).map(fx))
res223: (List[Int], Long) = (List(1, 2, 3),1205)
scala> go(List(1, 2, 3).map(fp >=> fxp).sequence.get)
res230: (List[Int], Long) = (List(1, 2, 3),402)
scala> go(List(1, 2, 3).map(f >>> fx))
res232: (List[Int], Long) = (List(1, 2, 3),1205)
20. DSL
import org.goldenport.dataflow._
val z = for (i <- table('T⽣生産実績)) yield {
val x = i.sum('原価) + 10
val y = i.masterJoin('M部⾨門)((x, y) => x('部⾨門Id) == y('部⾨門Id) and x('製品Id) == y('製品Id))
val z = y.update('NEW_GENKA, x)
val zz = z.where(x => x('部⾨門Id) == 1)
val zzz = zz.record(SymbolFieldDef('A), SymbolFieldDef('B))
zzz
}
println(”SQL = " + z.toSql.toText)
SQL = select sum(原価) + 10 as NEW_GENKA, A, B
from T⽣生産実績
left outer join M部⾨門 on (T⽣生産実績.部⾨門Id = M部⾨門.部⾨門Id) and (T⽣生産実績.製品Id = M部⾨門.
製品Id)
Where T⽣生産実績.部⾨門Id = 1
22. 代数的データ型
• Algebraic data type
• 直積の直和の総和
• 再帰構造
ケースクラスで直積を実現
case class Person(name: String, age: Int)
Case class Company(name: String, phone: String)
Eitherで直積の直和を実現
Either[Person, Company]
sealedトレイトで直積の直和の総和を実現
sealed trait Party
case class Person(name: String, age: Int) extends Party
case class Company(name: String, phone: String) extends Party
23. 永続データ構造
• Persistent data structure
• 変更更される際に変更更前のバージョンを常に保持するデータ構造で
ある。このようなデータ構造は、更更新の際に元のデータ構造を書
き換えるのではなく、新たなデータ構造を⽣生成すると考えられ、
イミュータブルなデータ構造の構築に利利⽤用可能である(Wikipedia)
24. 代数的構造デザインパターン
結合律律 (associative law)
• 半群 (semigroup)
• モノイド (monoid) (a + b) + c = a + (b + c)
• 群 (group)
可換律律 (commutative law)
• 可換半群
• 可換モノイド a+b=b+a
• 可換群(アーベル群)
分配律律 (distributive law)
• 環 (ring)
• 体 (field) a * (b + c) = a * b + a * c
25. 圏論論デザインパターン
圏 (category)
モナド • Hask圏 (Scala圏?)
(monad) • クライスリ圏
(kleisli category)
Applicative 射 (arrow,
functor morphism)
関⼿手
(functor)
26. 並列列プログラミング
• マルチスレッド
• 共有状態 (shared mutability)
• 共有状態をロック ← 伝統的⽅方法
• STM (Software Transactional Memory)
• アクター
• 状態をアクターローカル(スレッドローカル)にする (isolating
mutability)
• 不不変オブジェクトによるメッセージで通信
• 関数プログラミング⽅方式
• 代数的データ型、永続データ構造
• ⇒ 参照透過性、不不変オブジェクト
• 状態変更更ではなく、状態変更更命令令書を計算
• イメージとしてはSQLの⽂文字列列を計算して作成する感じ
• モナドのメカニズムを使って並列列処理理(+状態変更更命令令書)を
隠蔽
27. トピックス
• Monad
• http://modegramming.blogspot.jp/2012/08/30-13-
monad.html
• Monoid
• http://modegramming.blogspot.jp/2012/08/30-12-
monoid.html
• Fold
• http://modegramming.blogspot.jp/2012/07/30-10-
map-filter-fold.html
28. Monadicプログラミングの効⽤用
Java⾵風
def validate(name: String, age: Int): ValidationNEL[Throwable, (String,
Int)] = {!
val a = validateName(name) !
val b = validateAge(age) !
if (a.isSuccess && b.isSuccess) { !
val a1 = a.asInstanceOf[Success[NonEmptyList[Throwable], String]].a !
val b1 = b.asInstanceOf[Success[NonEmptyList[Throwable], Int]].a !
Success((a1, b1)) !
} else if (a.isSuccess) { !
b.asInstanceOf[Failure[NonEmptyList[Throwable], (String, Int)]] !
} else if (b.isSuccess) { !
a.asInstanceOf[Failure[NonEmptyList[Throwable], (String, Int)]] !
} else { !
val a1 = a.asInstanceOf[Failure[NonEmptyList[Throwable], String]].e !
val b1 = b.asInstanceOf[Failure[NonEmptyList[Throwable], Int]].e !
Failure(a1 |+| b1) !
} !
}!
29. Scala (関数型プログラミング)
def validate(name: String, age: Int):
ValidationNEL[Throwable, (String, Int)] = { !
validateName(name) match { !
case Success(a) => validateAge(age) match { !
case Success(b) => Success((a, b)) !
case Failure(e) => Failure(e) !
} !
case Failure(e1) => validateAge(age) match { !
case Success(b) => Failure(e1) !
case Failure(e2) => Failure(e1 |+| e2) !
} !
} !
} !
Scalaz (Monadicプログラミング)
def validate(name: String, age: Int):
ValidationNEL[Throwable, (String, Int)] = { !
(validateName(name) ⊛ validateAge(age))((_, _)) !
}!
URL: http://modegramming.blogspot.jp/2012/04/
scala-tips-validation-10-applicative.html
35. パイプラインの構成部品
メソッド メソッド動作 動作イメージ コンテナ型 要素型 要素数
map コンテナ内の要素に M[A]→M[B] 変わらない 変わる 同じ
関数を適⽤用して新し
いコンテナに詰め直
す。
filter コンテナ内の要素を M[A]→M[A] 変わらない 変わらない 同じ/減る
選別して新しコンテ
ナに詰め直す。
fold コンテナをまるごと M[A]→N 変わる N/A N/A
別のオブジェクトに
変換する。
reduce コンテナの内容を⼀一 M[A]→A 変わる N/A N/A
つにまとめる。
collect コンテナ内の要素に M[A]→M[B] 変わらない 変わる 同じ/減る
部分関数を適⽤用して
選択と変換を⾏行行う。
flatMap コンテナ内の要素ご M[A]→M[B] 変わらない 変わる 同じ/増える/減
とにコンテナを作成 る
する関数を適⽤用し最
後に⼀一つのコンテナ
にまとめる。
36. まとめ
• Monadicプログラミングの狙い
• 楽々プログラミング
• 並列列プログラミング
• DSL
• データフロー
• 関数型プログラミング
• ⼀一昔前の関数型プログラミングとは別物
• A→B、A→M[B]の関数が基本
• モナド、モノイド
• 型クラス
• Monadicプログラミング
• パイプラインで考えると関数型にアプローチしやすい
• データフローで業務モデルと繋げたい