Monadicプログラミング
マニアックス
2012年年8⽉月18⽇日
浅海智晴
活動
関連サイト
•  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/
⽂文脈
アジェンダ
Monadicプログラミングで
やりたいこと


 関数型プログラミング



   Monadicプログラミング
関連記事
•  Modegramming Style
  •  http://modegramming.blogspot.jp/
•  クラウド温泉3.0@⼩小樽〜~
     クラウド温泉3.0 (18) / Promise
諸元
•  スライド
 •  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で新しいバランスになるので、⾒見見極
    めが必要
新しい現実
ハードウェア

•  メニーコア、⼤大容量量メモリ、SSD
•  インメモリデータベース
•  並列列プログラミング

クラウド・プラットフォーム

•    クラウド・サービス、スマート・デバイス
•    故障、遅延
•    ⼤大規模データ、⼤大規模演算
•    ⾼高頻度度イベント
•    ⾮非同期、並列列、分散
•    NoSQL
アプリケーションの階層と役割

アプリケー   •  DSLの作法に従ってビジネスロ
           ジックを記述
 ション    •  OO、関数型のスキルは最低限


        •  フレームワークを簡単に使⽤用する
 DSL       ための専⽤用⾔言語
        •  OO、関数型の⾼高度度なスキル


フレーム    •  ドメインの共通処理理を記述
 ワーク    •  OO、関数型の⾼高度度なスキル
⽤用語
•  セッション内でのローカルな⽤用法

•  (普通の)オブジェクト指向⾔言語
 •  Java的なオブジェクト指向⾔言語。オブジェクト+クラス+
    抽象データ型+静的型付け+⼿手続き+値。
•  本物のオブジェクト指向⾔言語
 •  オブジェクトとメッセージから構成される核に何かを加え
    たもの。
•  関数型⾔言語
 •  純粋関数型⾔言語(での利利⽤用も可能)
   •  イミュータブル、参照透過性
 •  モナドが⼀一級市⺠民
 •  型クラス(相当)
Monadicプログラミングで
やりたいこと
Monadicプログラミングで
やりたいこと
•  定義
 •  普通
    •  Monadを使ったプログラミング。
 •  本セッションでの広い意味
    •  Monadを中⼼心としつつも、旧来からあるFunctorによるプログ
       ラミング、関数合成、コンビネータなども包含しつつ、パイプ
       ライン・プログラミングのスタイルとしてまとめたもの。
•  やりたいこと
 •  楽々プログラミング
 •  並列列プログラミング
 •  DSL
楽々プログラミング
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[部⾨門]))
}
 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),
                                            '親部⾨門名  -> "⾸首都圏統括"))
部⾨門(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(⾸首都圏統括))
並列列プログラミング
 準備

 時間を測る関数                                 テスト関数

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)
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)
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
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)
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
関数型プログラミング
代数的データ型
  •  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
永続データ構造
•  Persistent data structure
•  変更更される際に変更更前のバージョンを常に保持するデータ構造で
   ある。このようなデータ構造は、更更新の際に元のデータ構造を書
   き換えるのではなく、新たなデータ構造を⽣生成すると考えられ、
   イミュータブルなデータ構造の構築に利利⽤用可能である(Wikipedia)
代数的構造デザインパターン
結合律律 (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
圏論論デザインパターン

                         圏  (category)
          モナド            • Hask圏 (Scala圏?)
         (monad)         • クライスリ圏  
                          (kleisli category)




   Applicative                     射 (arrow,
    functor                        morphism)




                      関⼿手  
                   (functor)
並列列プログラミング
•  マルチスレッド  
   •  共有状態 (shared mutability)
   •  共有状態をロック ← 伝統的⽅方法
   •  STM (Software Transactional Memory)
•  アクター
   •  状態をアクターローカル(スレッドローカル)にする (isolating
      mutability)
   •  不不変オブジェクトによるメッセージで通信
•  関数プログラミング⽅方式
   •  代数的データ型、永続データ構造
   •  ⇒ 参照透過性、不不変オブジェクト
 •  状態変更更ではなく、状態変更更命令令書を計算
   •  イメージとしてはSQLの⽂文字列列を計算して作成する感じ
 •  モナドのメカニズムを使って並列列処理理(+状態変更更命令令書)を
    隠蔽
トピックス
•  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
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) !
   } !
}!
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
Monadicプログラミング
Arrowを⽤用いたデーターフロー
関数合成
モナドによる計算⽂文脈 (1)
モナドによる計算⽂文脈 (2)
パイプラインの構成部品
メソッド      メソッド動作        動作イメージ      コンテナ型   要素型     要素数

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]   変わらない   変わる     同じ/増える/減
          とにコンテナを作成                                 る
          する関数を適⽤用し最
          後に⼀一つのコンテナ
          にまとめる。
まとめ
•  Monadicプログラミングの狙い
 •  楽々プログラミング
 •  並列列プログラミング
 •  DSL
      •  データフロー
•  関数型プログラミング
 •    ⼀一昔前の関数型プログラミングとは別物
 •    A→B、A→M[B]の関数が基本
 •    モナド、モノイド
 •    型クラス
•  Monadicプログラミング
 •  パイプラインで考えると関数型にアプローチしやすい
 •  データフローで業務モデルと繋げたい
END

Monadicプログラミング マニアックス

  • 1.
  • 2.
  • 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/
  • 4.
  • 5.
  • 6.
    関連記事 •  Modegramming Style •  http://modegramming.blogspot.jp/ •  クラウド温泉3.0@⼩小樽〜~   クラウド温泉3.0 (18) / Promise
  • 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で新しいバランスになるので、⾒見見極 めが必要
  • 8.
    新しい現実 ハードウェア •  メニーコア、⼤大容量量メモリ、SSD •  インメモリデータベース • 並列列プログラミング クラウド・プラットフォーム •  クラウド・サービス、スマート・デバイス •  故障、遅延 •  ⼤大規模データ、⼤大規模演算 •  ⾼高頻度度イベント •  ⾮非同期、並列列、分散 •  NoSQL
  • 9.
    アプリケーションの階層と役割 アプリケー •  DSLの作法に従ってビジネスロ ジックを記述 ション •  OO、関数型のスキルは最低限 •  フレームワークを簡単に使⽤用する DSL ための専⽤用⾔言語 •  OO、関数型の⾼高度度なスキル フレーム •  ドメインの共通処理理を記述 ワーク •  OO、関数型の⾼高度度なスキル
  • 10.
    ⽤用語 •  セッション内でのローカルな⽤用法 •  (普通の)オブジェクト指向⾔言語 •  Java的なオブジェクト指向⾔言語。オブジェクト+クラス+ 抽象データ型+静的型付け+⼿手続き+値。 •  本物のオブジェクト指向⾔言語 •  オブジェクトとメッセージから構成される核に何かを加え たもの。 •  関数型⾔言語 •  純粋関数型⾔言語(での利利⽤用も可能) •  イミュータブル、参照透過性 •  モナドが⼀一級市⺠民 •  型クラス(相当)
  • 11.
  • 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
  • 21.
  • 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 datastructure •  変更更される際に変更更前のバージョンを常に保持するデータ構造で ある。このようなデータ構造は、更更新の際に元のデータ構造を書 き換えるのではなく、新たなデータ構造を⽣生成すると考えられ、 イミュータブルなデータ構造の構築に利利⽤用可能である(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
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 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プログラミング •  パイプラインで考えると関数型にアプローチしやすい •  データフローで業務モデルと繋げたい
  • 37.