76
関数型プログラミング 2014-05-11 at ゆかむ勉強会 Suguru Hamazaki Introduction to Scala Functional Programming

Introduction to Scala Functional Programming

Embed Size (px)

Citation preview

Page 1: Introduction to Scala Functional Programming

関数型プログラミング

2014-05-11 at ゆかむ勉強会 Suguru Hamazaki

Introduction to Scala Functional Programming

入�門

Page 2: Introduction to Scala Functional Programming

ご説明内容Agenda

Scalaって?

関数型プログラミングって?

ステートマシンをどうやって実装するの?

もっと抽象化できるよ!

便利な ライブラリーがあるよ!

Page 3: Introduction to Scala Functional Programming

プログラミング言語Scala Programming Language

Page 4: Introduction to Scala Functional Programming

オブジェクト指向と 関数型の融合

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

Page 5: Introduction to Scala Functional Programming

But …

Page 6: Introduction to Scala Functional Programming

パラダイムシフトは難しいA paradigm shift is difficult

Page 7: Introduction to Scala Functional Programming
Page 8: Introduction to Scala Functional Programming

関数型プログラミングFunctional Programming

Page 9: Introduction to Scala Functional Programming

純粋関数Pure Function

Page 10: Introduction to Scala Functional Programming

純粋関数Pure Function

• 関数の評価結果が引数の値のみによって決まり、同じ値を与えると常に同じ値の結果を返す

• 関数の評価によって、観測可能な副作用が発生しない

fx y

Page 11: Introduction to Scala Functional Programming

副作用の例Side effects examples

Page 12: Introduction to Scala Functional Programming

• 変数に再代入する

• データの構造を破壊的に変更する

• オブジェクトのフィールドに値をセットする

• 例外を投げる、エラー時に終了する

• コンソールに出力する、ユーザー入力を読む

• ファイルを読み書きする

• スクリーンに描画する

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

Page 13: Introduction to Scala Functional Programming

純粋関数プログラムを書くWrite programs using only pure functions

のみで

Page 14: Introduction to Scala Functional Programming

えっ?

Page 15: Introduction to Scala Functional Programming

コインのモデルA coin model

Page 16: Introduction to Scala Functional Programming

head tail

flip

flip

stay stay

Page 17: Introduction to Scala Functional Programming

オブジェクト指向的コイン

case class OoCoin(private var head: Boolean) { def flip() = { head = !head } def stay() = {} // do nothing def get = head}

Object-Oriented Coin

Page 18: Introduction to Scala Functional Programming

オブジェクト指向的コイン

case class OoCoin(private var head: Boolean) { def flip() = { head = !head } def stay() = {} // do nothing def get = head}

var (variable) で宣言し

たフィールドに再代入

Object-Oriented Coin

Page 19: Introduction to Scala Functional Programming

val c = OoCoin(true)c.flip()c.stay()c.flip()println("Showing a head? " + c.get)

Page 20: Introduction to Scala Functional Programming

参照透過性Referential Transparency

Page 21: Introduction to Scala Functional Programming

ある式の中で、その式の

値を変えることなく、等しいもの同士を置換 できること

Page 22: Introduction to Scala Functional Programming

val a = 5sumOfSquares(a + 1, a * 2)

def square(i: Int) = i * i def sumOfSquares(i1: Int, i2: Int) = square(i1) + square(i2)

Page 23: Introduction to Scala Functional Programming

val a = 5sumOfSquares(a + 1, a * 2)

def square(i: Int) = i * i def sumOfSquares(i1: Int, i2: Int) = square(i1) + square(i2)

純粋な関数

Page 24: Introduction to Scala Functional Programming

val a = 5sumOfSquares(a + 1, a * 2)

def square(i: Int) = i * i def sumOfSquares(i1: Int, i2: Int) = square(i1) + square(i2)

純粋な関数

再代入できない val

Page 25: Introduction to Scala Functional Programming

置換モデル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

Page 26: Introduction to Scala Functional Programming

再び、コインの例を見てみるOK, let’s go back to the coin model

Page 27: Introduction to Scala Functional Programming

val c = OoCoin(true)c.flip()c.stay()c.flip()println("Showing a head? " + c.get)

Page 28: Introduction to Scala Functional Programming

val c = OoCoin(true)c.flip()c.stay()c.flip()println("Showing a head? " + c.get)

mutable なデータ構造と非純粋関数

Page 29: Introduction to Scala Functional Programming

val c = OoCoin(true)c.flip()c.stay()c.flip()println("Showing a head? " + c.get)

mutable なデータ構造と非純粋関数

参照透過性は?

Page 30: Introduction to Scala Functional Programming

不変なコインと

純粋関数を使う

Use immutable coins

and pure functions

Page 31: Introduction to Scala Functional Programming

case class Coin(head: Boolean)!object Coin1 { def flip(c: Coin) = Coin(!c.head) def stay(c: Coin) = c}

Page 32: Introduction to Scala Functional Programming

case class Coin(head: Boolean)!object Coin1 { def flip(c: Coin) = Coin(!c.head) def stay(c: Coin) = c}

case class のフィー

ルドはデフォルトで

immutable

Page 33: Introduction to Scala Functional Programming

case class Coin(head: Boolean)!object Coin1 { def flip(c: Coin) = Coin(!c.head) def stay(c: Coin) = c}

case class のフィー

ルドはデフォルトで

immutable

受け取ったCoinは変更せず、新しいCoinを生成

Page 34: Introduction to Scala Functional Programming

val c0 = Coin(true)val c1 = flip(c0)val c2 = stay(c1)val c3 = flip(c2)println("Showing a head? " + c3.head)

Page 35: Introduction to Scala Functional Programming

val c0 = Coin(true)val c1 = flip(c0)val c2 = stay(c1)val c3 = flip(c2)println("Showing a head? " + c3.head)

Page 36: Introduction to Scala Functional Programming

コインの操作クラスで表現してみる

Express an ‘Action’ of a coin with a class

Page 37: Introduction to Scala Functional Programming

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) }}

Page 38: Introduction to Scala Functional Programming

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の状態を遷移さ

せる関数をラップ

Page 39: Introduction to Scala Functional Programming

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の状態を遷移さ

せる関数をラップ 自身も関数として扱える

Page 40: Introduction to Scala Functional Programming

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 を実行してから、次

の操作を実行する」という関数

Page 41: Introduction to Scala Functional Programming

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 を実行してから、次

の操作を実行する」という関数

Page 42: Introduction to Scala Functional Programming

First class Functions

• 引数として渡せる

• 戻り値として返せる

• 変数に代入できる

Page 43: Introduction to Scala Functional Programming

val flip = CoinAction(c => Coin(!c.head))val stay = CoinAction(c => c)

flip, stay を CoinAction の

インスタンスとして定義

Page 44: Introduction to Scala Functional Programming

val action = flip + stay + flipval c = action(Coin(true))println("Showing a head? " + c.head)

+() メソッドで操作を1つにまとめる

Page 45: Introduction to Scala Functional Programming

val action = flip + stay + flipval c = action(Coin(true))println("Showing a head? " + c.head)

+() メソッドで操作を1つにまとめる

でも、途中の結果も欲しい時は?

Page 46: Introduction to Scala Functional Programming

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) }}

Page 47: Introduction to Scala Functional Programming

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と一緒に 任意の型の結果を返す

Page 48: Introduction to Scala Functional Programming

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と一緒に 任意の型の結果を返す

遷移した結果を 任意の型に変換する

Page 49: Introduction to Scala Functional Programming

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と一緒に 任意の型の結果を返す

遷移した結果を 任意の型に変換する

次の操作と組み合わせる。ただし、前の結果を元に次の操作を生成する

関数として受け取る

Page 50: Introduction to Scala Functional Programming

val flip = CoinAction { c => val head = !c.head (Coin(head), head)}val stay = CoinAction(c => (c, c.head))

Page 51: Introduction to Scala Functional Programming

val flip = CoinAction { c => val head = !c.head (Coin(head), head)}val stay = CoinAction(c => (c, c.head))

Coinと一緒に Boolean 型の結果を

返す

Page 52: Introduction to Scala Functional Programming

val action = flip.flatMap { _ => stay.flatMap { _ => flip } }val (c, _) = action(Coin(true))println("Showing a head? " + c.head)

Page 53: Introduction to Scala Functional Programming

val action = flip.flatMap { _ => stay.flatMap { _ => flip } }val (c, _) = action(Coin(true))println("Showing a head? " + c.head)

flip + stay + flip と同様のコード

Page 54: Introduction to Scala Functional Programming

val action = flip.flatMap { _ => stay.flatMap { _ => flip } }val (c, _) = action(Coin(true))println("Showing a head? " + c.head)

flip + stay + flip と同様のコード

ただし、単なる CoinAction ではなく、CoinAction を返す関数を渡している

Page 55: Introduction to Scala Functional Programming

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)

Page 56: Introduction to Scala Functional Programming

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() で変換

Page 57: Introduction to Scala Functional Programming

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() が受けるのは、今の操作の結果から、次回以降の操作を作る関数。

Page 58: Introduction to Scala Functional Programming

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 がネストした内部ブロックで利用

できる

Page 59: Introduction to Scala Functional Programming

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 が使える

Page 60: Introduction to Scala Functional Programming

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)

Page 61: Introduction to Scala Functional Programming

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] に変換して

しまう

Page 62: Introduction to Scala Functional Programming

CoinActionを抽象化

Page 63: Introduction to Scala Functional Programming

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) }}

Page 64: Introduction to Scala Functional Programming

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 のメソッドをどこからも呼び出してない

Page 65: Introduction to Scala Functional Programming

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/ より抜粋、一部改変

Page 66: Introduction to Scala Functional Programming

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 として定義

Page 67: Introduction to Scala Functional Programming

先ほどと同じように使えます

Page 68: Introduction to Scala Functional Programming

Wait …

Page 69: Introduction to Scala Functional Programming

それでできるよScalaz

Page 70: Introduction to Scala Functional Programming

Scalaz

Scalaz provides purely functional data structures to complement those

from the Scala standard library.— from http://typelevel.org/projects/scalaz/

Page 71: Introduction to Scala Functional Programming

import scalaz.Statetype CoinAction[A] = State[Coin, A]

scalaz の State を使う

Page 72: Introduction to Scala Functional Programming

先ほどと同じように使えます

Page 73: Introduction to Scala Functional Programming

まとめSummary

ScalaはOOとFPがミックスした マルチパラダイム言語

関数型プログラミングでは純粋関数を使う

状態遷移を表わすCoinAction を作り、ステート

マシンを実装した

CoinAction を State として抽象化

Scalaz の State を紹介

Page 74: Introduction to Scala Functional Programming

詳しい解説ドキュメント& 完全なソースコードはこちら

https://github.com/hamazy/scala-fp-calisthenics

Page 75: Introduction to Scala Functional Programming

Q&A

Page 76: Introduction to Scala Functional Programming

Image Credits