Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

実務者のためのかんたんScalaz

9,735 views

Published on

Scala

Published in: Technology

実務者のためのかんたんScalaz

  1. 1. 実務者のための! かんたんScalaz 2014年年7⽉月7⽇日 Everforth 浅海智晴
  2. 2. シリーズ •  テーマ •  クラウドアプリケーション開発技術の導⼊入 •  今後の予定 •  実務者のためのかんたんScalaプログラミング •  実務者のためのかんたんScala⼊入出⼒力力プログラミング •  実務者のためのかんたんScala設計 •  実務者のためのかんたんオブジェクト指向分析/設計 •  実務者のためのかんたん業務分析
  3. 3. 今⽇日のテーマ •  Monadicプログラミングの認知 •  概要、必要性 •  Scalaz導⼊入の⼊入り⼝口 •  必要最⼩小限の知識識&具体的な便便利利さ •  Scalazの便便利利機能 •  Monoid •  関数型プログラミングのパワーを知る •  実作業にすぐに適⽤用可
  4. 4. ⽂文脈
  5. 5. アジェンダ ⽂文脈 関数型プログラミング Object Functional Programming Scalaz
  6. 6. 新しい現実 •  メニーコア、⼤大容量量メモリ、SSD •  インメモリデータベース •  並列列プログラミング ハードウェア •  クラウド・サービス、スマート・デバイス •  故障、遅延 •  ⼤大規模データ、⼤大規模演算 •  ⾼高頻度度イベント、ストリーミング •  ⾮非同期、並列列、分散 •  NoSQL クラウド・プラットフォーム
  7. 7. OFADの要素技術/関連技術 OFADOOAD OFP クラウド   コンピュー ティング アジャイル 開発 UCD/UX Scala 計算機科学 DSL DDD DCI CQRS EIP EDA はセッションで触れる技術
  8. 8. アプリケーションの階層と役割 •  DSLの作法に従ってビジネスロ ジックを記述 •  OO、関数型のスキルは最低限 アプリケー ション •  フレームワークを簡単に使⽤用する ための専⽤用⾔言語 •  OO、関数型の⾼高度度なスキル DSL •  ドメインの共通処理理を記述 •  OO、関数型の⾼高度度なスキル フレーム ワーク
  9. 9. Scalaz •  https://github.com/scalaz/scalaz •  キャッチフレーズ •  昔: Scalaz: Type Classes and Pure Functional Data Structures for Scala •  今: An extension to the core Scala library for functional programming. http://typelevel.org •  最新の関数型プログラミングを可能にする機能群を Scala向けに⽤用意 •  型クラス •  純粋関数型データ構造
  10. 10. 関数型プログラミング
  11. 11. 関数型⾔言語とは •  ⾼高階関数を扱える。 •  関数を値として扱える。 •  関数の引数と返却値に関数を渡せる。 •  関数リテラル(クロージャ)が記述できる。 •  数学(ラムダ計算、数理理論論理理学、圏論論など)的にプログラ ムを記述できる。
  12. 12. 関数型⾔言語の系譜 元祖関数型⾔言語 • pure Lisp • ラムダ計算 伝統的関数型 ⾔言語 • Lisp, ML, OCaml • ラムダ計算 • ⼿手続き、オブジェ クト指向で補完 • 抽象データ型 • Subtype polymorphism 新世代関数型 ⾔言語 • Haskell • Scala(+scalaz) • ラムダ計算 • 代数、圏論論 • 型クラス • 代数データ型、 モナド • Parametric polymorphism 浅海私⾒見見。 20年年ほどの空⽩白の後の⾒見見え⽅方、 あるいはOOプログラマが後追い で調べたときの⾒見見え⽅方と考えて ください。
  13. 13. 関数型⾔言語の⻑⾧長所と短所 •  ⾼高階関数を使った技が使える •  List, 関数合成(コンビネータ)など •  定理理と証明 •  証明された(動作保証された)定理理(関数)を積み上げてプログラ ムを記述できる  (← 多少理理想論論も⼊入ってます) ⻑⾧長所 •  関数実⾏行行のオーバーヘッド •  関数オブジェクト •  メモリを⼤大量量に消費する •  関数オブジェクト •  データの⼤大量量複写 •  スタックの使⽤用量量が読めない •  再帰 短所
  14. 14. 関数型⾔言語の技術マップ
  15. 15. 代数的構造デザインパターン •  半群 (semigroup) •  モノイド  (monoid) •  群 (group) 結合律律 (associative law) •  可換半群 •  可換モノイド •  可換群(アーベル群) 可換律律 (commutative law) •  環  (ring) •  体 (field) 分配律律 (distributive law) (a + b) + c = a + (b + c) a + b = b + a a * (b + c) = a * b + a * c
  16. 16. 圏論論デザインパターン 圏  (category) • Hask圏 (Scala圏?) • クライスリ圏   (kleisli category) 射 (arrow, morphism) 関⼿手   (functor) Applicative functor モナド   (monad)
  17. 17. 関数型を導⼊入する⽬目的 •  プログラミング効率率率 •  部品化・再利利⽤用 •  DSL •  データフロー •  並列列/並⾏行行プログラミング •  ⼤大規模データ処理理 •  ストリーミングデータ処理理 •  イベント駆動プログラミング
  18. 18. 関数型プログラミングの3つの技術 •  関数(「A → B」)を積み上げてプログラムを書く技術 •  パイプライン・プログラミング •  直感的でそれほど難しいわけではない •  モナドを使って関数をシステムと結びつける技術 •  難解 •  利利⽤用パターンは決まっているので、アプリケーション・プ ログラマ的には、イディオムを覚えておけば⼗十分 •  オブジェクト指向分析・設計と結びつける技術 •  「実務者のためのかんたんオブジェクト指向分析・設計」
  19. 19. Scalazを実務に展開する戦略略 •  便便利利機能を活⽤用 •  Monoid •  データ構造の導⼊入 •  Tree, NonEmptyList •  EphemeralStream •  Cord, Rope •  Foldable, Traverse •  Monadicプログラミング •  並列列/並⾏行行プログラミング •  ストリームプログラミング
  20. 20. A → B •  関数の基本 •  型Aの値を型Bの値に置き換える (置き換えモデル, substitution model) •  副作⽤用はないので、A→Bの関数の外側にいかなる影響も 与えない (no side effect) •  型Aの値が同じなら、いかなるタイミングで呼び出して も型Bの同じ値が返る (参照透過性, referential transparency) •  圏論論:射(arrow, morphism) •  論論理理学:ならば(imply) •  def f(x: A): B
  21. 21. A → B → C •  引数が2つある関数 •  「A → B」の形の関数(引数が1つ)の合成で記述 •  A → (B → C) •  Aを引数に取り「B→Cの関数」を返す関数 •  def f(a: A, b: B): C •  def f(a: A)(b: B): C •  def f(a: A): B => C •  val f: A => B => C
  22. 22. A → A → A •  「A → B → C」の特殊な形 •  A, B, Cが同じ型 •  ⼆二項演算⼦子 •  f(x: A, y: A): A •  1 + 1 → 2 •  “abc” + “xyz” → “abcxyz” •  List(“abc”) ++ List(“xyz”) → List(“abc”, “xyz”)
  23. 23. A → M[B] •  「A → B」の特殊な形 •  「B」の部分が「M[B]」となっている •  Mはモナド、モナドMがBをくるんでいる形 •  モナドのbind演算で使⽤用する •  def flatMap[A, B](f: A => List[B])
  24. 24. モナド •  難解な概念念 •  http://ja.wikibooks.org/wiki/Haskell/%E5%9C%8F%E8%AB%96 •  ⾮非公式理理解 •  型安全汎⽤用フレームワーク基底クラス
  25. 25. Monadicプログラミングの効⽤用 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) ! } ! }! Java⾵風
  26. 26. 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) ! } ! } ! } ! def validate(name: String, age: Int): ValidationNEL[Throwable, (String, Int)] = { ! (validateName(name) ⊛ validateAge(age))((_, _)) ! }! Scala (関数型プログラミング) Scalaz (Monadicプログラミング) URL: http://modegramming.blogspot.jp/2012/04/ scala-tips-validation-10-applicative.html
  27. 27. パイプライン・プログラミング
  28. 28. Arrowを⽤用いたデーターフロー
  29. 29. 関数合成
  30. 30. モナドによる計算⽂文脈 (1)
  31. 31. モナドによる計算⽂文脈 (2)
  32. 32. パイプラインの構成部品 メソッド メソッド動作 動作イメージ コンテナ型 要素型 要素数 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] 変わらない 変わる 同じ/増える/減 る
  33. 33. 代数的データ型 •  Algebraic data type •  直積の直和の総和 •  再帰構造 case class Person(name: String, age: Int) Case class Company(name: String, phone: String) ケースクラスで直積を実現 Either[Person, Company] Eitherで直積の直和を実現 sealedトレイトで直積の直和の総和を実現 sealed trait Party case class Person(name: String, age: Int) extends Party case class Company(name: String, phone: String) extends Party
  34. 34. 永続データ構造 •  Persistent data structure •  変更更される際に変更更前のバージョンを常に保持するデータ構造で ある。このようなデータ構造は、更更新の際に元のデータ構造を書 き換えるのではなく、新たなデータ構造を⽣生成すると考えられ、 イミュータブルなデータ構造の構築に利利⽤用可能である(Wikipedia)
  35. 35. 並列列プログラミング •  マルチスレッド   •  共有状態 (shared mutability) •  共有状態をロック ← 伝統的⽅方法 •  STM (Software Transactional Memory) •  アクター •  状態をアクターローカル(スレッドローカル)にする (isolating mutability) •  不不変オブジェクトによるメッセージで通信 •  関数プログラミング⽅方式 •  代数的データ型、永続データ構造 •  ⇒ 不不変オブジェクト •  状態変更更ではなく、状態変更更命令令書を計算 •  イメージとしてはSQLの⽂文字列列を計算して作成する感じ •  モナドのメカニズムを使って並列列処理理(+状態変更更命令令書)を 隠蔽
  36. 36. 並列列プログラミング def go[T](a: => T): (T, Long) = { val start = System.currentTimeMillis val r = a val end = System.currentTimeMillis (r, end - start) } val f = (x: Int) => { Thread.sleep(x * 100) x } scala> go(f(10)) res176: (Int, Long) = (10,1000) scala> go { | f(10) | } res253: (Int, Long) = (10,1001) 準備 時間を測る関数 テスト関数
  37. 37. scala> val fp = f.promise fp: scalaz.Kleisli[scalaz.concurrent.Promise,Int,Int] = scalaz.Kleislis$$anon$1@9edaab8 scala> go((1.some |@| 2.some |@| 3.some)(_ + _ + _).get) res237: (Int, Long) = (6,1) scala> go(f(1) + f(2) + f(3)) res212: (Int, Long) = (6,603) scala> go((fp(1) |@| fp(2) |@| fp(3))(_ + _ + _).get) res215: (Int, Long) = (6,302) PromiseのKleisli Applicative
  38. 38. scala> go(List(1, 2, 3).map(f).map(f)) res221: (List[Int], Long) = (List(1, 2, 3),1205) scala> go(List(1, 2, 3).map(fp).map(_.flatMap(fp)).sequence.get) res220: (List[Int], Long) = (List(1, 2, 3),602) val fx = (x: Int) => { val t = math.abs(x - 4) Thread.sleep(t * 100) x } val fxp = fx.promise テスト関数
  39. 39. scala> go(List(1, 2, 3).map(f >>> fx)) res232: (List[Int], Long) = (List(1, 2, 3),1205) 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(fp).map(_.flatMap(fxp)).sequence.get) res222: (List[Int], Long) = (List(1, 2, 3),402)
  40. 40. DSL Anorm Squeryl Slick SQL("""select name from coffees where price < 10.0""”) from(coffees)(s => where(s.price < 10.0) select(s)) coffees.filter(_.price < 10.0).map(_.name)
  41. 41. 関数型プログラミングの秘密 •  不不変(immutable)なのに状態が持て、状態遷移を記述で きる理理由 •  不不変(immutable)なのにデータ構造の変更更ができる理理由 •  副作⽤用なし(no side)なのに外部⼊入出⼒力力ができる理理由
  42. 42. scalaz stream •  com.everforth.lib.util.FileBag for { bag <- resource.managed(new FileBag()) bag2 <- resource.managed(new FileBag()) bag3 <- resource.managed(new FileBag()) } { Process.constant(4096).through(io.chunkR(new URL("http://scala- lang.org/").openStream)).to(bag.chunkW).run.run bag.chunksR(4096).to(bag2.chunkW).run.run bag2.linesR.map(_ + "n").pipe(text.utf8Encode).to(bag3.chunkW).run.run bag.size should be (bag2.size) bag.size should be (bag3.size) }
  43. 43. Spark •  RDD (Resilient Distributed Dataset) •  http://www.cs.berkeley.edu/~pwendell/strataconf/api/core/ spark/RDD.html scala> val textFile = sc.textFile("README.md") textFile: spark.RDD[String] = spark.MappedRDD@2ee9b6e3 scala> val linesWithSpark = textFile.filter(line => line.contains("Spark")) linesWithSpark: spark.RDD[String] = spark.FilteredRDD@7dd4af09 scala> textFile.filter(line => line.contains("Spark")).count() // How many lines contain "Spark"? res3: Long = 15
  44. 44. RxJava - Scala •  Functional Reactive Programming •  https://github.com/Netflix/RxJava •  http://techblog.netflix.com/2013/02/rxjava-netflix-api.html def simpleComposition() { // fetch an asynchronous Observable<String> // that emits 75 Strings of 'anotherValue_#' customObservableNonBlocking() // skip the first 10 .skip(10) // take the next 5 .take(5) // transform each String with the provided function .map({ stringValue -> return stringValue + "_transformed"}) // subscribe to the sequence and print each transformed String .subscribe({ println "onNext => " + it}) }
  45. 45. Object  Functional   Programming
  46. 46. OFP新三種の神器 •  mix-in •  型安全のAOP的な運⽤用 トレイト  (trait) •  計算⽂文脈をカプセル化する新しい⾔言語概念念 •  Monadicプログラミング モナド  (monad) •  型安全のダブルディスパッチ(?) •  Scalaでは、⽂文脈、主体、客体の組でオブジェクトを束縛 型クラス  (type class)
  47. 47. オブジェクトと関数の連携(1)
  48. 48. オブジェクトと関数の連携(2)
  49. 49. 関数と⼿手続き •  関数は副作⽤用なし •  副作⽤用があるものは「⼿手続き」 •  例例外も副作⽤用の⼀一種 •  厳密には例例外を発⽣生させる可能性のあるものは「⼿手続き」 •  プログラミングの⼿手間を考えて例例外は特別扱いしたい •  本セッションでは「準関数」を導⼊入 要素 副作⽤用なし 参照透過性 例例外なし 関数 ◯ ◯ ◯ 準関数 ◯ ◯ × ⼿手続き ー ー ー
  50. 50. 計算領領域を分ける •  純粋関数型  :: 例例外は使⽤用せずモナドを⽤用いて例例外状態を 管理理する •  準関数型A :: Tryモナド(準モナド)を使⽤用する •  準関数型B :: 例例外を使⽤用する •  ⼿手続き型  :: 普通のプログラミング
  51. 51. エラーハンドリング戦略略 •  オブジェクト指向⽅方式 •  例例外(Throwable)を使⽤用 •  try, catch, finally •  関数型⽅方式 •  モナド系のコンテナを使⽤用する 種別 意味 モナド モナド機能を持っているコンテナ 準モナド モナド機能を持っているがモナド則を⼀一部満たしていない ⾮非モナド モナド機能を持っていないコンテナ
  52. 52. エラーハンドリング コンテナ 種別 機能 パッケージ Option モナド 失敗/成功の⽂文脈を保持 scala Try 準モナド 例例外状態を保持 scala.util Either ⾮非モナド 直和 scala.util Validation モナド バリデーション scalaz / モナド 直和(右側を正としてモナド 化) scalaz Future 準モナド ⾮非同期(即時) scala.concurren t Promise モナド ⾮非同期(即時) scalaz.concurre nt Task モナド ⾮非同期(遅延) scalaz.concurre nt
  53. 53. Scalaz かんたん
  54. 54. 便便利利機能: Boolean scala> def bt: Boolean = true bt: Boolean scala> def bf: Boolean = false bf: Boolean scala> bt option 100 res8: Option[Int] = Some(100) scala> bf option 100 res9: Option[Int] = None scala> bt ?? 100 res11: Int = 100 scala> bf ?? 100 res10: Int = 0 scala> bt !? 100 res12: Int = 0 scala> bf !? 100 res13: Int = 100 かんたん
  55. 55. 便便利利機能: Option scala> val os = 100.some os: Option[Int] = Some(100) scala> val on = none[Int] on: Option[Int] = None scala> os ? "defined" | "undefined” res14: String = defined scala> on ? "defined" | "undefined” res15: String = undefined scala> on.orZero res16: Int = 0 scala> os | -1 res18: Int = 100 scala> on | -1 res17: Int = -1 scala> os.cata(_ + 1, -1) res19: Int = 101 scala> on.cata(_ + 1, -1) res20: Int = -1 かんたん
  56. 56. Monoid •  代数的構造の概念念 •  http://ja.wikipedia.org/wiki/ %E3%83%A2%E3%83%8E%E3%82%A4%E3%83%89 •  A → A → A •  2項演算⼦子 •  結合法則 •  単位元 •  演算 •  1 + 2 = 3 •  “abc” + “mno” = “abcmno” •  List(“abc”) ++ List(“mno”) = List(“abcmno”)
  57. 57. 結合法則 •  結合法則 •  (1 + 2) + 3 == 1 + (2 + 3) •  (“abc” + “mno”) + “xyz” == “abc” + (“mno” + “xyz”) •  単位元 •  1 + 0 == 0 + 1 == 1 •  “abc” + “” == “” + abc” == “abc
  58. 58. Monoidを使⽤用しないロジック def sumi(xs: Vector[Int]): Int = sumi2(xs, 0) def sumi2(xs: Vector[Int], acc: Int): Int = { xs.headOption match { case None => acc case Some(x) => sum(xs.tail, acc + x) } }
  59. 59. Monoidを使⽤用したロジック def summ[T: Monoid](xs: Vector[T]): T = { val M = implicitly(Monoid[T]) summ2(xs, M.zero) } def summ2[T: Monoid](xs: Vector[T], acc: T): T = { xs.headOption match { case None => acc case Some(x) => summ2(xs.tail, acc |+| x) } }
  60. 60. 便便利利機能: Monoid scala> val xs = Vector(1, 2, 3) val xs = Vector(1, 2, 3) xs: scala.collection.immutable.Vector[Int] = Vector(1, 2, 3) scala> xs.concatenate xs.concatenate res3: Int = 6 scala> xs.foldLeft(0) { (z, x) => z + x } xs.foldLeft(0) { (z, x) => z + x } res4: Int = 6 scala> xs.foldLeft(0) { (z, x) => z |+| x } xs.foldLeft(0) { (z, x) => z |+| x } res5: Int = 6 かんたん
  61. 61. 便便利利機能: Monoid scala> xs.foldMap(x => x) res87: Int = 6 scala> xs.foldMap(x => x + 1) res88: Int = 9 かんたん
  62. 62. まとめ •  Scalazの導⼊入を可能にする関数型プログラミングの基礎 知識識について簡単に紹介しました。 •  現時点では聞いておくだけでOK •  まず導⼊入して欲しいScalaz機能を紹介しました。 •  便便利利機能 •  前提知識識不不要で便便利利に使えます。 •  Monoid •  Scalazを使った最新の関数型プログラミングのパワーを前提知 識識不不要で実感できます。
  63. 63. END

×