Scalaプログラミング
マニアックス
2012年年11⽉月30⽇日
浅海智晴
活動
関連サイト
•  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/
⽂文脈
アジェンダ
関数型プログラミン
グ

 Object-Functional
 Programming

   Monadicプログラミ
   ング
諸元
•  スライド
 •  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、関数型の⾼高度度なスキル
関数型プログラミング
関数型⾔言語とは
•  ⾼高階関数を扱える。
 •  関数を値として扱える。
 •  関数の引数と返却値に関数を渡せる。
 •  関数リテラル(クロージャ)が記述できる。




•  数学(ラムダ計算、圏論論など)的にプログラムを記述でき
   る。
関数型⾔言語の系譜
浅海私⾒見見。
20年年ほどの空⽩白の後の⾒見見え⽅方、
あるいはOOプログラマが後追い
で調べたときの⾒見見え⽅方と考えて
ください。
                                           新世代関数型
                                           ⾔言語
                                           •  Haskell
                      伝統的関数型               •  Scala(+scalaz)
                      ⾔言語                  •  ラムダ計算
                      •  Lisp, ML, OCaml   •  代数、圏論論
                      •  ラムダ計算             •  型クラス
                      •  ⼿手続き、オブジェ            •  代数データ型、
       元祖関数型⾔言語          クト指向で補完                 モナド
       •  pure Lisp      •  抽象データ型            •  Parametric
                         •  Subtype              polymorphism
       •  ラムダ計算
                            polymorphism
関数型⾔言語の⻑⾧長所と短所
 ⻑⾧長所

 •  ⾼高階関数を使った技が使える
    •  List処理理, 関数合成(コンビネータ)、モナドなど
 •  定理理と証明
    •  証明された(動作保証された)定理理(関数)を積み上げてプログラムを
       記述できる  (← 多少理理想論論も⼊入ってます)

 短所

 •  関数実⾏行行のオーバーヘッド
    •  関数オブジェクト
 •  メモリを⼤大量量に消費する
    •  関数オブジェクト
    •  データの⼤大量量複写
 •  スタックの使⽤用量量が読めない
    •  再帰
    •  回避する技のノウハウが必要
関数型⾔言語の技術マップ
代数的データ型
  •  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)
型クラス
•  3つのポリモーフィズム
   •  サブタイプ・ポリモーフィズム
   •  OOのポリモーフィズム
 •  パラメタ・ポリモーフィズム
   •  ジェネリックタイプ
 •  アドホック・ポリモーフィズム
•  浅海の理理解
   •  アドホック・ポリモーフィズムを使って、サブタイプ・ポリ
      モーフィズム的なことを型の決定時に⾏行行う
   •  関数型⾔言語でオブジェクト指向のサブタイプ・ポリモーフィズ
      ム的なことができる
   •  ただし、コンパイル時に型が確定するのがOOとの違い
•  型クラスを使うと、呼び出し元と呼び出し先と呼ばれる関数
   を疎結合にできる
   •  システムの拡張性
   •  主な使⽤用例例:代数的構造を既存のデータ構造に適⽤用する
並列列プログラミング
•  マルチスレッド  
   •  共有状態 (shared mutability)
   •  共有状態をロック ← 伝統的⽅方法
   •  STM (Software Transactional Memory)
•  アクター
   •  状態をアクターローカル(スレッドローカル)にする (isolating
      mutability)
   •  不不変オブジェクトによるメッセージで通信
•  関数プログラミング⽅方式
   •  代数的データ型、永続データ構造
   •  ⇒ 不不変オブジェクト
 •  状態変更更ではなく、状態変更更命令令書を計算
   •  イメージとしてはSQLの⽂文字列列を計算して作成する感じ
 •  モナドのメカニズムを使って並列列処理理(+状態変更更命令令書)を
    隠蔽
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
トピックス
•  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
Arrowを⽤用いたデーターフロー
モナドによる計算⽂文脈
パイプラインの構成部品
メソッド      メソッド動作        動作イメージ      コンテナ型   要素型     要素数

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]   変わらない   変わる     同じ/増える/減
          とにコンテナを作成                                 る
          する関数を適⽤用し最
          後に⼀一つのコンテナ
          にまとめる。
Object  Functional  
Programming  (OFP)
オブジェクトと関数


   ⼿手続き      関数      論論理理




          オブジェクト指向




 Java      Scala
OFP新三種の神器
 トレイト  (trait)

•  mix-in
•  型安全のAOP的な運⽤用

 モナド  (monad)

•  計算⽂文脈をカプセル化する新しい⾔言語概念念
•  Monadicプログラミング

 型クラス  (type class)

•  型安全のダブルディスパッチ(?)
•  Scalaでは、⽂文脈、主体、客体の組でオブジェクトを束縛
トレイト
•  mixinのためのクラス的なもの
•  クラスの継承に制限をつけることで、多重継承的なこと
   ができるようになる
•  Scalaプログラミングの要
 •  Cakeパターン
•  モジュール化
 •  型安全Dependency Injection
 •  http://www.crosson.org/2012/03/dependency-
    injection-using-scala-traits.html
 •  http://www.warski.org/blog/2010/12/di-in-scala-cake-
    pattern/
 •  http://jonasboner.com/2008/10/06/real-world-scala-
    dependency-injection-di/
内部DSL for Framework
•  Scalable Language = Scala
•  APIからDSLへ



         アプリケー                 アプリケー
 API      ション                   ション    DSL

         ライブラリ
                                ⾔言語
            ⾔言語
オブジェクトと関数の連携(1)
オブジェクトと関数の連携(2)
DSLの例:SparkとScalding
val file = spark.textFile("hdfs://...")

file.flatMap(line => line.split(" "))
    .map(word => (word, 1))
                                     Spark
    .reduceByKey(_ + _)


class WordCountJob(args : Args) extends Job(args) {
  TextLine( args("input") ).read.
   flatMap('line -> 'word) { line : String => line.split("s+") }.
   groupBy('word) { _.size }.
   write( Tsv( args("output") ) )
}

                            Scalding
                            •  https://github.com/twitter/scalding
                            •  CascadingのScala DSL
                            •  Collection APIでHadoop演算
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)
まとめ
•  関数型プログラミングの部品
  •    代数的データ型
  •    永続データ構造
  •    モナド
  •    型クラス
•  オブジェクト指向プログラミングの新技術
  •  トレイト
•  OFP
  •  オブジェクト指向で関数型を束ねる
•  DSL
  •  Scalable Laguage = Scala
•  Scala
  •  OFPをやるならScalaしか選択肢がないかも
END

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

  • 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.
    諸元 •  スライド • 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で新しいバランスになるので、⾒見見極 めが必要
  • 7.
    新しい現実 ハードウェア •  メニーコア、⼤大容量量メモリ、SSD •  インメモリデータベース • 並列列プログラミング クラウド・プラットフォーム •  クラウド・サービス、スマート・デバイス •  故障、遅延 •  ⼤大規模データ、⼤大規模演算 •  ⾼高頻度度イベント •  ⾮非同期、並列列、分散 •  NoSQL
  • 8.
    アプリケーションの階層と役割 アプリケー •  DSLの作法に従ってビジネスロ ジックを記述 ション •  OO、関数型のスキルは最低限 •  フレームワークを簡単に使⽤用する DSL ための専⽤用⾔言語 •  OO、関数型の⾼高度度なスキル フレーム •  ドメインの共通処理理を記述 ワーク •  OO、関数型の⾼高度度なスキル
  • 9.
  • 10.
    関数型⾔言語とは •  ⾼高階関数を扱える。 • 関数を値として扱える。 •  関数の引数と返却値に関数を渡せる。 •  関数リテラル(クロージャ)が記述できる。 •  数学(ラムダ計算、圏論論など)的にプログラムを記述でき る。
  • 11.
    関数型⾔言語の系譜 浅海私⾒見見。 20年年ほどの空⽩白の後の⾒見見え⽅方、 あるいはOOプログラマが後追い で調べたときの⾒見見え⽅方と考えて ください。 新世代関数型 ⾔言語 •  Haskell 伝統的関数型 •  Scala(+scalaz) ⾔言語 •  ラムダ計算 •  Lisp, ML, OCaml •  代数、圏論論 •  ラムダ計算 •  型クラス •  ⼿手続き、オブジェ •  代数データ型、 元祖関数型⾔言語 クト指向で補完 モナド •  pure Lisp •  抽象データ型 •  Parametric •  Subtype polymorphism •  ラムダ計算 polymorphism
  • 12.
    関数型⾔言語の⻑⾧長所と短所 ⻑⾧長所 • ⾼高階関数を使った技が使える •  List処理理, 関数合成(コンビネータ)、モナドなど •  定理理と証明 •  証明された(動作保証された)定理理(関数)を積み上げてプログラムを 記述できる  (← 多少理理想論論も⼊入ってます) 短所 •  関数実⾏行行のオーバーヘッド •  関数オブジェクト •  メモリを⼤大量量に消費する •  関数オブジェクト •  データの⼤大量量複写 •  スタックの使⽤用量量が読めない •  再帰 •  回避する技のノウハウが必要
  • 13.
  • 14.
    代数的データ型 • 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
  • 15.
    永続データ構造 •  Persistent datastructure •  変更更される際に変更更前のバージョンを常に保持するデータ構造で ある。このようなデータ構造は、更更新の際に元のデータ構造を書 き換えるのではなく、新たなデータ構造を⽣生成すると考えられ、 イミュータブルなデータ構造の構築に利利⽤用可能である(Wikipedia)
  • 16.
    代数的構造デザインパターン 結合律律 (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
  • 17.
    圏論論デザインパターン 圏  (category) モナド   • Hask圏 (Scala圏?) (monad) • クライスリ圏   (kleisli category) Applicative 射 (arrow, functor morphism) 関⼿手   (functor)
  • 18.
    型クラス •  3つのポリモーフィズム •  サブタイプ・ポリモーフィズム •  OOのポリモーフィズム •  パラメタ・ポリモーフィズム •  ジェネリックタイプ •  アドホック・ポリモーフィズム •  浅海の理理解 •  アドホック・ポリモーフィズムを使って、サブタイプ・ポリ モーフィズム的なことを型の決定時に⾏行行う •  関数型⾔言語でオブジェクト指向のサブタイプ・ポリモーフィズ ム的なことができる •  ただし、コンパイル時に型が確定するのがOOとの違い •  型クラスを使うと、呼び出し元と呼び出し先と呼ばれる関数 を疎結合にできる •  システムの拡張性 •  主な使⽤用例例:代数的構造を既存のデータ構造に適⽤用する
  • 19.
    並列列プログラミング •  マルチスレッド   •  共有状態 (shared mutability) •  共有状態をロック ← 伝統的⽅方法 •  STM (Software Transactional Memory) •  アクター •  状態をアクターローカル(スレッドローカル)にする (isolating mutability) •  不不変オブジェクトによるメッセージで通信 •  関数プログラミング⽅方式 •  代数的データ型、永続データ構造 •  ⇒ 不不変オブジェクト •  状態変更更ではなく、状態変更更命令令書を計算 •  イメージとしてはSQLの⽂文字列列を計算して作成する感じ •  モナドのメカニズムを使って並列列処理理(+状態変更更命令令書)を 隠蔽
  • 20.
    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) ! } ! }!
  • 21.
    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
  • 22.
    トピックス •  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
  • 23.
  • 24.
  • 25.
    パイプラインの構成部品 メソッド メソッド動作 動作イメージ コンテナ型 要素型 要素数 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] 変わらない 変わる 同じ/増える/減 とにコンテナを作成 る する関数を適⽤用し最 後に⼀一つのコンテナ にまとめる。
  • 26.
  • 27.
    オブジェクトと関数 ⼿手続き 関数 論論理理 オブジェクト指向 Java Scala
  • 28.
    OFP新三種の神器 トレイト  (trait) • mix-in •  型安全のAOP的な運⽤用 モナド  (monad) •  計算⽂文脈をカプセル化する新しい⾔言語概念念 •  Monadicプログラミング 型クラス  (type class) •  型安全のダブルディスパッチ(?) •  Scalaでは、⽂文脈、主体、客体の組でオブジェクトを束縛
  • 29.
    トレイト •  mixinのためのクラス的なもの •  クラスの継承に制限をつけることで、多重継承的なこと ができるようになる •  Scalaプログラミングの要 •  Cakeパターン •  モジュール化 •  型安全Dependency Injection •  http://www.crosson.org/2012/03/dependency- injection-using-scala-traits.html •  http://www.warski.org/blog/2010/12/di-in-scala-cake- pattern/ •  http://jonasboner.com/2008/10/06/real-world-scala- dependency-injection-di/
  • 30.
    内部DSL for Framework • Scalable Language = Scala •  APIからDSLへ アプリケー アプリケー API ション ション DSL ライブラリ ⾔言語 ⾔言語
  • 31.
  • 32.
  • 33.
    DSLの例:SparkとScalding val file =spark.textFile("hdfs://...") file.flatMap(line => line.split(" ")) .map(word => (word, 1)) Spark .reduceByKey(_ + _) class WordCountJob(args : Args) extends Job(args) { TextLine( args("input") ).read. flatMap('line -> 'word) { line : String => line.split("s+") }. groupBy('word) { _.size }. write( Tsv( args("output") ) ) } Scalding •  https://github.com/twitter/scalding •  CascadingのScala DSL •  Collection APIでHadoop演算
  • 34.
  • 35.
    Monadicプログラミングで やりたいこと •  定義 • 普通 •  Monadを使ったプログラミング。 •  本セッションでの広い意味 •  Monadを中⼼心としつつも、旧来からあるFunctorによるプログ ラミング、関数合成、コンビネータなども包含しつつ、パイプ ライン・プログラミングのスタイルとしてまとめたもの。 •  やりたいこと •  楽々プログラミング •  並列列プログラミング •  DSL
  • 36.
    楽々プログラミング 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[部⾨門])) }
  • 37.
     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), '親部⾨門名  -> "⾸首都圏統括"))
  • 38.
    部⾨門(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(⾸首都圏統括))
  • 39.
    並列列プログラミング 準備 時間を測る関数 テスト関数 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)
  • 40.
    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)
  • 41.
    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
  • 42.
    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)
  • 43.
    まとめ •  関数型プログラミングの部品 •  代数的データ型 •  永続データ構造 •  モナド •  型クラス •  オブジェクト指向プログラミングの新技術 •  トレイト •  OFP •  オブジェクト指向で関数型を束ねる •  DSL •  Scalable Laguage = Scala •  Scala •  OFPをやるならScalaしか選択肢がないかも
  • 44.