Your SlideShare is downloading. ×
  • Like
Introduction to Scala Functional Programming
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Now you can save presentations on your phone or tablet

Available for both IPhone and Android

Text the download link to your phone

Standard text messaging rates apply

Introduction to Scala Functional Programming

  • 362 views
Published

 

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
  • さいとうさん、コメントありがとうございます。何かのお役に立てば嬉しいです。
    Are you sure you want to
    Your message goes here
  • お久しぶりです
    プログラミングは専門外なのですが、参考にさせてもらいます
    また、おしえてください
    Are you sure you want to
    Your message goes here
    Be the first to like this
No Downloads

Views

Total Views
362
On SlideShare
0
From Embeds
0
Number of Embeds
1

Actions

Shares
Downloads
3
Comments
2
Likes
0

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. 関数型プログラミング 2014-05-11 at ゆかむ勉強会 Suguru Hamazaki Introduction to Scala Functional Programming 入�門
  • 2. ご説明内容 Agenda Scalaって? 関数型プログラミ ングって? ステートマシンをどう やって実装するの? もっと抽象化 できるよ! 便利な ライブラリーがあるよ!
  • 3. プログラミング言語 Scala Programming Language
  • 4. オブジェクト指向と 関数型の融合 Have the best of both worlds. Construct elegant class hierarchies for maximum code reuse and extensibility, implement their behavior using higher-order functions. Or anything in-between. — from http://www.scala-lang.org/ Object-Oriented Meets Functional
  • 5. But …
  • 6. パラダイムシフトは難しい A paradigm shift is difficult
  • 7. 関数型プログラミング Functional Programming
  • 8. 純粋関数 Pure Function
  • 9. 純粋関数 Pure Function • 関数の評価結果が引数の値のみに よって決まり、同じ値を与えると 常に同じ値の結果を返す • 関数の評価によって、観測可能な 副作用が発生しない f x y
  • 10. 副作用の例Side effects examples
  • 11. • 変数に再代入する • データの構造を破壊的に変更する • オブジェクトのフィールドに値をセットする • 例外を投げる、エラー時に終了する • コンソールに出力する、ユーザー入力を読む • ファイルを読み書きする • スクリーンに描画する Variable reassigning Destructive mutation Setting field values Throwing exceptions or stopping at failure Console output or reading user input File I/O Displaying to screen
  • 12. 純粋関数プログラムを書く Write programs using only pure functions のみで
  • 13. えっ?
  • 14. コインのモデル A coin model
  • 15. head tail flip flip stay stay
  • 16. オブジェクト指向的コイン case class OoCoin(private var head: Boolean) { def flip() = { head = !head } def stay() = {} // do nothing def get = head } Object-Oriented Coin
  • 17. オブジェクト指向的コイン case class OoCoin(private var head: Boolean) { def flip() = { head = !head } def stay() = {} // do nothing def get = head } var (variable) で宣言し たフィールドに再代入 Object-Oriented Coin
  • 18. val c = OoCoin(true) c.flip() c.stay() c.flip() println("Showing a head? " + c.get)
  • 19. 参照透過性 Referential Transparency
  • 20. ある式の中で、その式の 値を変えることなく、 等しいもの同士を 置換できること
  • 21. val a = 5 sumOfSquares(a + 1, a * 2) def square(i: Int) = i * i def sumOfSquares(i1: Int, i2: Int) = square(i1) + square(i2)
  • 22. val a = 5 sumOfSquares(a + 1, a * 2) def square(i: Int) = i * i def sumOfSquares(i1: Int, i2: Int) = square(i1) + square(i2) 純粋な関数
  • 23. val a = 5 sumOfSquares(a + 1, a * 2) def square(i: Int) = i * i def sumOfSquares(i1: Int, i2: Int) = square(i1) + square(i2) 純粋な関数 再代入できない val
  • 24. 置換モデル sumOfSquares(a + 1, a * 2) sumOfSquares(5 + 1, 5 * 2) sumOfSquares(6, 10) square(6) + square(10) (6 * 6) + (10 * 10) 36 + 100 136 Substitution Model http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-10.html
  • 25. 再び、コインの例を見てみる OK, let’s go back to the coin model
  • 26. val c = OoCoin(true) c.flip() c.stay() c.flip() println("Showing a head? " + c.get)
  • 27. val c = OoCoin(true) c.flip() c.stay() c.flip() println("Showing a head? " + c.get) mutable なデータ構造と 非純粋関数
  • 28. val c = OoCoin(true) c.flip() c.stay() c.flip() println("Showing a head? " + c.get) mutable なデータ構造と 非純粋関数 参照透過性は?
  • 29. 不変なコインと 純粋関数を使う Use immutable coins and pure functions
  • 30. case class Coin(head: Boolean) ! object Coin1 { def flip(c: Coin) = Coin(!c.head) def stay(c: Coin) = c }
  • 31. case class Coin(head: Boolean) ! object Coin1 { def flip(c: Coin) = Coin(!c.head) def stay(c: Coin) = c } case class のフィー ルドはデフォルトで immutable
  • 32. case class Coin(head: Boolean) ! object Coin1 { def flip(c: Coin) = Coin(!c.head) def stay(c: Coin) = c } case class のフィー ルドはデフォルトで immutable 受け取ったCoinは変更せず、新しいCoinを生成
  • 33. val c0 = Coin(true) val c1 = flip(c0) val c2 = stay(c1) val c3 = flip(c2) println("Showing a head? " + c3.head)
  • 34. val c0 = Coin(true) val c1 = flip(c0) val c2 = stay(c1) val c3 = flip(c2) println("Showing a head? " + c3.head)
  • 35. コインの 操作 クラスで表現してみる を Express an ‘Action’ of a coin with a class
  • 36. case class CoinAction(action: Coin => Coin) extends (Coin => Coin) { def apply(c: Coin) = action(c) def +(next: CoinAction): CoinAction = CoinAction { c0 => val c1 = action(c0) next(c1) } }
  • 37. case class CoinAction(action: Coin => Coin) extends (Coin => Coin) { def apply(c: Coin) = action(c) def +(next: CoinAction): CoinAction = CoinAction { c0 => val c1 = action(c0) next(c1) } } Coinの状態を遷移さ せる関数をラップ
  • 38. case class CoinAction(action: Coin => Coin) extends (Coin => Coin) { def apply(c: Coin) = action(c) def +(next: CoinAction): CoinAction = CoinAction { c0 => val c1 = action(c0) next(c1) } } Coinの状態を遷移さ せる関数をラップ 自身も関数として扱える
  • 39. case class CoinAction(action: Coin => Coin) extends (Coin => Coin) { def apply(c: Coin) = action(c) def +(next: CoinAction): CoinAction = CoinAction { c0 => val c1 = action(c0) next(c1) } } Coinの状態を遷移さ せる関数をラップ 自身も関数として扱える 「コインを受け取った時に、自 身の action を実行してから、次 の操作を実行する」という関数
  • 40. case class CoinAction(action: Coin => Coin) extends (Coin => Coin) { def apply(c: Coin) = action(c) def +(next: CoinAction): CoinAction = CoinAction { c0 => val c1 = action(c0) next(c1) } } Coinの状態を遷移さ せる関数をラップ 自身も関数として扱える 関数として呼び出された際に、 ラップした関数を実行 「コインを受け取った時に、自 身の action を実行してから、次 の操作を実行する」という関数
  • 41. First class Functions • 引数として渡せる • 戻り値として返せる • 変数に代入できる
  • 42. val flip = CoinAction(c => Coin(!c.head)) val stay = CoinAction(c => c) flip, stay を CoinAction の インスタンスとして定義
  • 43. val action = flip + stay + flip val c = action(Coin(true)) println("Showing a head? " + c.head) +() メソッドで操作を 1つにまとめる
  • 44. val action = flip + stay + flip val c = action(Coin(true)) println("Showing a head? " + c.head) +() メソッドで操作を 1つにまとめる でも、途中の結果も 欲しい時は?
  • 45. case class CoinAction[A](action: Coin => (Coin, A)) extends (Coin => (Coin, A)) { def apply(c: Coin) = action(c) def +[B](next: CoinAction[B]): CoinAction[B] = flatMap(_ => next) def map[B](f: A => B): CoinAction[B] = CoinAction { c0 => val (c1, a) = apply(c0) (c1, f(a)) } def flatMap[B](f: A => CoinAction[B]): CoinAction[B] = CoinAction { c0 => val (c1, a) = apply(c0) f(a)(c1) } }
  • 46. case class CoinAction[A](action: Coin => (Coin, A)) extends (Coin => (Coin, A)) { def apply(c: Coin) = action(c) def +[B](next: CoinAction[B]): CoinAction[B] = flatMap(_ => next) def map[B](f: A => B): CoinAction[B] = CoinAction { c0 => val (c1, a) = apply(c0) (c1, f(a)) } def flatMap[B](f: A => CoinAction[B]): CoinAction[B] = CoinAction { c0 => val (c1, a) = apply(c0) f(a)(c1) } } Coinと一緒に任意の型の結果を返す
  • 47. case class CoinAction[A](action: Coin => (Coin, A)) extends (Coin => (Coin, A)) { def apply(c: Coin) = action(c) def +[B](next: CoinAction[B]): CoinAction[B] = flatMap(_ => next) def map[B](f: A => B): CoinAction[B] = CoinAction { c0 => val (c1, a) = apply(c0) (c1, f(a)) } def flatMap[B](f: A => CoinAction[B]): CoinAction[B] = CoinAction { c0 => val (c1, a) = apply(c0) f(a)(c1) } } Coinと一緒に任意の型の結果を返す 遷移した結果を 任意の型に変換する
  • 48. case class CoinAction[A](action: Coin => (Coin, A)) extends (Coin => (Coin, A)) { def apply(c: Coin) = action(c) def +[B](next: CoinAction[B]): CoinAction[B] = flatMap(_ => next) def map[B](f: A => B): CoinAction[B] = CoinAction { c0 => val (c1, a) = apply(c0) (c1, f(a)) } def flatMap[B](f: A => CoinAction[B]): CoinAction[B] = CoinAction { c0 => val (c1, a) = apply(c0) f(a)(c1) } } Coinと一緒に任意の型の結果を返す 遷移した結果を 任意の型に変換する 次の操作と組み合わせる。ただし、 前の結果を元に次の操作を生成する 関数として受け取る
  • 49. val flip = CoinAction { c => val head = !c.head (Coin(head), head) } val stay = CoinAction(c => (c, c.head))
  • 50. val flip = CoinAction { c => val head = !c.head (Coin(head), head) } val stay = CoinAction(c => (c, c.head)) Coinと一緒に Boolean 型の結果を 返す
  • 51. val action = flip.flatMap { _ => stay.flatMap { _ => flip } } val (c, _) = action(Coin(true)) println("Showing a head? " + c.head)
  • 52. val action = flip.flatMap { _ => stay.flatMap { _ => flip } } val (c, _) = action(Coin(true)) println("Showing a head? " + c.head) flip + stay + flip と 同様のコード
  • 53. val action = flip.flatMap { _ => stay.flatMap { _ => flip } } val (c, _) = action(Coin(true)) println("Showing a head? " + c.head) flip + stay + flip と 同様のコード ただし、単なる CoinActionではなく、CoinAction を返 す関数を渡している
  • 54. val action = flip.flatMap { b1 => stay.flatMap { _ => flip.map { b3 => (b1, b3) } } } val (_, (b1, b3)) = action(Coin(true)) println("1st occurrence is a head? " + b1) println("3rd occurrence is a head? " + b3)
  • 55. val action = flip.flatMap { b1 => stay.flatMap { _ => flip.map { b3 => (b1, b3) } } } val (_, (b1, b3)) = action(Coin(true)) println("1st occurrence is a head? " + b1) println("3rd occurrence is a head? " + b3) 結果が Tuple になるよう map() で変換
  • 56. val action = flip.flatMap { b1 => stay.flatMap { _ => flip.map { b3 => (b1, b3) } } } val (_, (b1, b3)) = action(Coin(true)) println("1st occurrence is a head? " + b1) println("3rd occurrence is a head? " + b3) 結果が Tuple になるよう map() で変換 flatMap() が受けるのは、今の操作の結果から、次回以降の操作を作る関数。
  • 57. val action = flip.flatMap { b1 => stay.flatMap { _ => flip.map { b3 => (b1, b3) } } } val (_, (b1, b3)) = action(Coin(true)) println("1st occurrence is a head? " + b1) println("3rd occurrence is a head? " + b3) 結果が Tuple になるよう map() で変換 flatMap() が受けるのは、今の操作の結果から、次回以降の操作を作る関数。 なので、b1, b3 がネスト した内部ブロックで利用 できる
  • 58. val action = for { b1 <- flip _ <- stay b3 <- flip } yield (b1, b3) val (_, (b1, b3)) = action(Coin(true)) println("1st occurrence is a head? " + b1) println("3rd occurrence is a head? " + b3) map(), flatMap() があれば、for-comprehension が使える
  • 59. val action = for { s1 <- flip.map(b1 => "1st occurrence is a head? " + b1) _ <- stay s3 <- flip.map(b3 => "3rd occurrence is a head? " + b3) } yield (s1 + "n" + s3) val (_, s) = action(Coin(true)) println(s)
  • 60. val action = for { s1 <- flip.map(b1 => "1st occurrence is a head? " + b1) _ <- stay s3 <- flip.map(b3 => "3rd occurrence is a head? " + b3) } yield (s1 + "n" + s3) val (_, s) = action(Coin(true)) println(s) この時点で、 CoinAction[Boolean] を CoinAction[String] に変換して しまう
  • 61. CoinActionを抽象化
  • 62. case class CoinAction[A](action: Coin => (Coin, A)) extends (Coin => (Coin, A)) { def apply(c: Coin) = action(c) def +[B](next: CoinAction[B]): CoinAction[B] = flatMap(_ => next) def map[B](f: A => B): CoinAction[B] = CoinAction { c0 => val (c1, a) = apply(c0) (c1, f(a)) } def flatMap[B](f: A => CoinAction[B]): CoinAction[B] = CoinAction { c0 => val (c1, a) = apply(c0) f(a)(c1) } }
  • 63. case class CoinAction[A](action: Coin => (Coin, A)) extends (Coin => (Coin, A)) { def apply(c: Coin) = action(c) def +[B](next: CoinAction[B]): CoinAction[B] = flatMap(_ => next) def map[B](f: A => B): CoinAction[B] = CoinAction { c0 => val (c1, a) = apply(c0) (c1, f(a)) } def flatMap[B](f: A => CoinAction[B]): CoinAction[B] = CoinAction { c0 => val (c1, a) = apply(c0) f(a)(c1) } } Coin のメソッドをどこ からも呼び出してない
  • 64. case class State[S, +A](run: S => (S, A)) { def map[B](f: A => B): State[S, B] = State { s => val (s2, a) = run(s) (s2, f(a)) } def flatMap[B](f: A => State[S, B]): State[S, B] = State { s => val (s2, a) = run(s) f(a).run(s2) } } Coin を型パラメーター S として抽象化 Functional Programming in Scala http://www.manning.com/bjarnason/ より抜粋、一部改変
  • 65. type CoinAction[A] = State[Coin, A] val flip: CoinAction[Boolean] = State { c => val head = !c.head (Coin(head), head) } val stay: CoinAction[Boolean] = State(c => (c, c.head)) CoinAction を type alias として定義
  • 66. 先ほどと同じように 使えます
  • 67. Wait …
  • 68. それ でできるよ Scalaz
  • 69. Scalaz Scalaz provides purely functional data structures to complement those from the Scala standard library. — from http://typelevel.org/projects/scalaz/
  • 70. import scalaz.State type CoinAction[A] = State[Coin, A] scalaz の State を使う
  • 71. 先ほどと同じように 使えます
  • 72. まとめ Summary ScalaはOOとFPがミックスした マルチパラダイム言語 関数型プログラミングでは 純粋関数を使う 状態遷移を表わす CoinAction を作り、ステート マシンを実装した CoinAction を State として抽象化 Scalaz の State を紹介
  • 73. 詳しい解説ドキュメント& 完全なソースコードはこちら https://github.com/hamazy/scala-fp-calisthenics
  • 74. Q&A
  • 75. Image Credits