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

6,497 views

Published on

Scala

Published in: Technology
0 Comments
41 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
6,497
On SlideShare
0
From Embeds
0
Number of Embeds
1,351
Actions
Shares
0
Downloads
19
Comments
0
Likes
41
Embeds 0
No embeds

No notes for slide

実務者のためのかんたん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

×