SlideShare une entreprise Scribd logo
1  sur  56
Introduction to
Functional Programming
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
株式会社ピクセラ
先端技術開発部
2019年08月19日
関数とコンテナ型クラス
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
2
Function
Type A => Type B へのマッピング
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
3
map (メソッドの方)
Functionはマッピングなのに mapメソッド?
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
4
List::map
List[1, 2, 3].map(i => i + 1) => List[2, 3, 4]
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
5
ダイアグラム
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
6
A BNormal
f
ダイアグラム
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
7
A B
List[A] List[B]
Normal
Lifted
f
List::map コンテナ層に適用可能にする
List
List[A] = Head of List :: Tail of List
Tail of List = (Head of (Tail of List)) :: (Tail of (Tail of List))
Tail of Tail of List = …
Tail of Tail of … = Nil
-> Head::TofHead::TTofHead::…Nil
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
8
List (scala的)
sealed trait List[+A]
case object Nil extends List[Nothing]
case class Cons[+A](head: A, tail: List[A]) extends List[A]
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
9
fold[Right|Left]
def foldRight[A, B](as: List[A], z:B)(f: (A, B) => B) : B = as match {
case Nil => z
case Cons(x, xs) => f(x, foldRight(xs, z)(f))
}
@annotation.tailrec
def foldLeft[A, B](as: List[A], z:B)(f: (B, A) => B) : B = as match {
case Nil => z
case Cons(x, xs) => foldLeft(xs, f(z, x))(f)
}
関数fが多数回呼ばれる => fに副作用を入れないことがいかに重要か
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
10
Folds are primitive functions for List class
def List::map[A, B](la:List[A])(f: A -> B) : List[B]=
foldRight(Nil[List[B]])((h, t) => f(h) :: t))
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
11
ダイアグラム
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
12
A B
List[A] List[B]
Normal
Lifted
入力
f
入力がList[A]の場合、
f にある操作をして
出力をList[List[B]]ではなく、
List[B] にするには?
List要素 1つ1つで
List[B]ができるf
ダイアグラム
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
13
A B
List[A] List[B]
Normal
Lifted
入力
f
f’
f’ を作るには?
List::flatMap (scala)
def append[A](la: List[A], lb: List[A]): List[A] = la match {
case Nil => lb
case Cons(h, t) => Cons(h, append(t, lb))
}
def List::flatMap[A, B] (ls: List[A])(f: A => List[B]): List[B] =
foldRight(ls, Nil:List[B])((h, t) => append(f(h), t))
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
14
ダイアグラム
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
15
A B
List[A] List[B]
Normal
Lifted
入力
f
f’
List::flatMap
命令型記述と関数型記述
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
16
Imperative Programming Style
TypeA a = funAFromUnit();
TypeB b = funAToB(a);
TypeC c = …
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
17
表現方法の変換
chain(x:input, f: x => y) = f(input)
chain(funAFromUnit(), a =>
chain(funAToB(a), b =>
chain(fun…)))
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
18
Functional Programming Style
chain(funAFromUnit(), a =>
chain(funAToB(a), b =>
chain(fun…)))
funAFromUnit() を入力とし、後続全てを丸ごとFunctionであると
捉えられる。 後続中も同様の構造となる。 == 再帰的
Imperative的表現 を Functional 的表現で再現した。
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
19
Functionalの有利な点
chain が function
=>function中で操作が可能。
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
20
この有利な部分を有効に使いたい。
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
21
例 日常のコード
def proc_nest(a:A): C = {
val b = funAtoB(a)
if (isValid(b) == true) {
val c = funBtoC(b)
if (isValid(c) == true) c else Nothing
}
Nothing
}
例 日常のコード
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
23
def proc_flat(a: A) : C = {
val b = funAtoB(a)
if (isValid(b) != true) {
Nothing
}
val c = funBtoC(b)
if (isValid(c) != true) {
Nothing
}
c
}
問題点
関数が本来やりたい記述が埋もれる
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
24
別の大問題 => Exception の存在
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
25
Function的問題点
○ 関数IF中にExceptionの情報は無い。
proc: A=>C
Exceptionは副作用
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
26
ターゲット
関数本来の処理記述が埋もれさせず
かつ
(見た目上)例外を発生させない
表現にしたい
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
27
Railway Programming
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
28
元ネタ https://fsharpforfunandprofit.com/posts/recipe-part2/
Left(e)
Right(b;B)a:A
f
sealed trait Either[+E, +A]{
…
}
case class Left[+E](value: E) extends Either[E, Nothing]
case class Right[+A](value:A) extends Either[Nothing, A]
Optionalの親戚的
Railway function
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
29
def railway[A, B, E](f: A => B): A => Either[E, B] = a => {
try {
if (a == Nothing) {
Right(Nothing)
}
val result = f(a)
if (isValid(result) == true) Right(result)
else Right(Nothing)
} catch(exception e) {
Left(e)
}
}
それぞれを
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
30
Left(e)
Right(c)b
railway(funBtoC)
Left(e)
Right(b)a
railway(funAtoB)
連結できたら良くない?
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
31
Left(e)
Right(c)b
Left(e)
Right(b)a
しかし
railway(funAtoB) と railway(funBtoC)の入出力の型式が違う
railway(funAtoB) railway(funBtoC)
ダイアグラム
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
32
A B
Either[E, B]
Normal
Lifted
funAtoB
railway(funAtoB)
C
Either[E, C]
railway(funBtoC)
funBtoC
ダイアグラム
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
33
A B
Either[E, B]
Normal
Lifted
funAtoB
railway(funAtoB)
C
Either[E, C]
railway(funBtoC)
funBtoC
Either::flatMap
flatMap 実装
sealed trait Either[+E, +A]{
def flatMap[EE >: E, B](f : A => Either[EE, B]):
Either[EE, B] = this match {
case Left(e) => Left(e) // * point
case Right(a) => f(a)
}
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
34
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
35
Left(e)
Right(c)Right(b)
Left(e)
Right(b)a
railway(funAtoB) Either::flatMap(railway(funBtoC))
Left(e)
Left(e)
Right(c)
railway(funAtoB) railway(funBtoC)Either::flatMap
a
railway(funAtoB) railway(funBtoC)Either::flatMap
一旦 Leftに分岐すると、 何も処理されずスルーされる。
Left(e)
Right(c)a
*point
例 日常のコード 改
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
38
def proc_rail[A, C, E](a: A) : Either[E, C] = {
Right(a).flatMap(a =>
railway(funAtoB)(a).flatMap(b =>
railway(funBtoC)(b)))
}
演算子擬似コード
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
39
def (>>=)[A,B,E](eith:Either[E, A])(f:A => Either[E,
B]):Either[E, B] = {
eith.flatMap(a => f(a))
}
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
40
def proc_rail[A, C, E](a: A) : Either[E, C] = {
Right(a)
>>= railway(funAtoB)
>>= railway(funBtoC)
}
演算子>>= を使った擬似コード
ダイアグラム
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
41
A B
Either[E, B]
Normal
Lifted
railway(funAtoB)
C
Either[E, C]
railway(funBtoC)
Either[A, E]
結合則条件
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
42
Right(a).flatMap(f).flatMap(g) ==
Right(a).flatMap(aa => f(aa).flatMap(g))
→ flatMapの並列結合したものと、 ネストしたflatMapが同値
これと、単位元の存在条件を満たすコンテナクラスの持つ特徴
Monad!
Monadは、計算の継続を抽象化したもの。
わかりやすい単語表現 “Chainable”
flatMap は 別名 bind と言われることもある。
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
43
Either::map
def Either::map[A, B](ea:Either[E, A])(f: A => B): Either[E, B]
= ea.flatMap(a => Right(f(a)))
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
44
さらなる応用
List::map(la:List[A], f:A => Either[E, A]): List[Either[E, A]]
List[Right[A], Left[E], Right[A], …] となる場合を考える。
この時、 Listの中にLeftが出てくることを許容できないケースがある
とする。
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
45
List[Either[E, A]] が、 Either[E, List[A]] となるようにしたい。
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
46
map2
def Either::map2[E, A, B, C](ea: Either[E, A], eb: Either[E, B],
f: (A, B) =>C) : Either[E, C] =
ea.flatMap(a =>
eb.map(b =>
f(a, b)
))
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
47
ちょっと脱線 zip
Either::zip[E, A, B](ea: Either[A], eb: Either[E, B]) : Either[E,
(A, B)] =
Either::map2(ea, eb) ((a, b) => (a, b))
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
48
sequence
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
49
def sequence[E, A](es: List[Either[E, A]]): Either[E, List[A]] =
es.foldRight[Either[E,List[A]]](Right(Nil))((head_e, tail_e) =>
Either::map2(head_e)(tail_e)((head, tail) => head::tail))
ダイアグラム sequence
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
50
A ANormal
Either[A, E]
List[Either[A, E]] Either[List[A], E]
List[A, E]
まとめ
○ mapのような高階関数はListだけでなく、様々なコンテナ型クラスに実装可能
○ 同じような計算表現の高階関数を定義できる。
○ 命令型記述を関数型記述で実現可能となるだけでなく、行間に追加機能を持たせられる。
最上位の表現においては追加機能の詳細記述が隠蔽される。
○ コンテナ型クラスを多層に出来、さらに層の入れ替えを実現する演算がある。
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
51
追加情報
○ 普段使うコンテナ型クラスはmonad性質がある。
→ 普段のプログラムは、全てmonad性質を持つコンテナ型クラスで構成できる。
○ List, Stream (遅延評価)
○ Optional(==Nullable), Either
○ State
○ Async, Parallel
(︎↑を基本とし、さらに上位のライブラリを作れる。
例. テストライブラリ(テストデータ自動生成 & データに対する性質テスト)
パーサーライブラリ(500行程度でパーサーライブラリ&Jsonパーサ合わせたものがかけます)
○ Reader-monad, Writer-Monad, IO-Monad 等もある
○ Monad(継続 == 命令型的記述)に関連して、関数型言語では、特別な記述様式が用意されている
Haskell : do記法, Scala: for記法, F#: computation記法
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
52
追加情報
○ unit, flatMap をprimitiveとして、上位Combinatorを実装可能なものが多い。
○ map, mapN, zip, sequnence, transverse…
○ unit, map2 をprimitiveにすることも可能 primitiveの選択性)
○ コンテナ型クラスのメソッドがある規則を満たせば、それらクラスがさらに抽象的なグループに分別できる
例 Functor 属性
map に対する規則 containr::map[A](c:container, f: A => A) について、
container::map(c, (a => a)) == a
が成立するもの
○ 演算に対しても同様にグループ分けもできる。
例 Monoid 属性
結合則 op(op(x, y), z) == op(x, op(y, z)) と単位元 の存在
実例 String , List の加算
=> foldable との関係性につながる
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
53
所感
メリット:
○ FPはプログラムを統一型式で表現可能な手段を与えてくれる。
→ “統一“というプログラマがコーディング重要指標にあげる点を、徹底的に実現可能。
→ 言語を超えた統一表現
○ FPがもつ真のmodular性
デメリット:
学習コストの高さ
FP以外にも、FPと同様の統一型式表現手段があればそれを選択すれば良いですが、恐らくそれも学習コストの高さがあると思います。
 結局 2択
皆さんが普段戦っている、プログラムを組む時の大問題”複雑性”に対して
* いわゆる普通の命令型の表現を選択して、一つ一つの処理は簡単だが、結果複雑で大きくなり易いコードを書いていく
or
* 高い学習コストを払ってでも新しい表現を選択し、複雑性を隠蔽できるコードを書いていくか
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
54
参考書 & 参考サイト
https://www.manning.com/books/functional-programming-in-scala
https://fsharpforfunandprofit.com
Introduction to Functional Programming

Introduction to Functional Programming

  • 1. Introduction to Functional Programming Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 株式会社ピクセラ 先端技術開発部 2019年08月19日
  • 2. 関数とコンテナ型クラス Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 2
  • 3. Function Type A => Type B へのマッピング Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 3
  • 4. map (メソッドの方) Functionはマッピングなのに mapメソッド? Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 4
  • 5. List::map List[1, 2, 3].map(i => i + 1) => List[2, 3, 4] Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 5
  • 6. ダイアグラム Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 6 A BNormal f
  • 7. ダイアグラム Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 7 A B List[A] List[B] Normal Lifted f List::map コンテナ層に適用可能にする
  • 8. List List[A] = Head of List :: Tail of List Tail of List = (Head of (Tail of List)) :: (Tail of (Tail of List)) Tail of Tail of List = … Tail of Tail of … = Nil -> Head::TofHead::TTofHead::…Nil Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 8
  • 9. List (scala的) sealed trait List[+A] case object Nil extends List[Nothing] case class Cons[+A](head: A, tail: List[A]) extends List[A] Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 9
  • 10. fold[Right|Left] def foldRight[A, B](as: List[A], z:B)(f: (A, B) => B) : B = as match { case Nil => z case Cons(x, xs) => f(x, foldRight(xs, z)(f)) } @annotation.tailrec def foldLeft[A, B](as: List[A], z:B)(f: (B, A) => B) : B = as match { case Nil => z case Cons(x, xs) => foldLeft(xs, f(z, x))(f) } 関数fが多数回呼ばれる => fに副作用を入れないことがいかに重要か Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 10
  • 11. Folds are primitive functions for List class def List::map[A, B](la:List[A])(f: A -> B) : List[B]= foldRight(Nil[List[B]])((h, t) => f(h) :: t)) Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 11
  • 12. ダイアグラム Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 12 A B List[A] List[B] Normal Lifted 入力 f 入力がList[A]の場合、 f にある操作をして 出力をList[List[B]]ではなく、 List[B] にするには? List要素 1つ1つで List[B]ができるf
  • 13. ダイアグラム Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 13 A B List[A] List[B] Normal Lifted 入力 f f’ f’ を作るには?
  • 14. List::flatMap (scala) def append[A](la: List[A], lb: List[A]): List[A] = la match { case Nil => lb case Cons(h, t) => Cons(h, append(t, lb)) } def List::flatMap[A, B] (ls: List[A])(f: A => List[B]): List[B] = foldRight(ls, Nil:List[B])((h, t) => append(f(h), t)) Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 14
  • 15. ダイアグラム Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 15 A B List[A] List[B] Normal Lifted 入力 f f’ List::flatMap
  • 16. 命令型記述と関数型記述 Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 16
  • 17. Imperative Programming Style TypeA a = funAFromUnit(); TypeB b = funAToB(a); TypeC c = … Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 17
  • 18. 表現方法の変換 chain(x:input, f: x => y) = f(input) chain(funAFromUnit(), a => chain(funAToB(a), b => chain(fun…))) Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 18
  • 19. Functional Programming Style chain(funAFromUnit(), a => chain(funAToB(a), b => chain(fun…))) funAFromUnit() を入力とし、後続全てを丸ごとFunctionであると 捉えられる。 後続中も同様の構造となる。 == 再帰的 Imperative的表現 を Functional 的表現で再現した。 Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 19
  • 20. Functionalの有利な点 chain が function =>function中で操作が可能。 Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 20
  • 21. この有利な部分を有効に使いたい。 Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 21
  • 22. 例 日常のコード def proc_nest(a:A): C = { val b = funAtoB(a) if (isValid(b) == true) { val c = funBtoC(b) if (isValid(c) == true) c else Nothing } Nothing }
  • 23. 例 日常のコード Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 23 def proc_flat(a: A) : C = { val b = funAtoB(a) if (isValid(b) != true) { Nothing } val c = funBtoC(b) if (isValid(c) != true) { Nothing } c }
  • 24. 問題点 関数が本来やりたい記述が埋もれる Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 24
  • 25. 別の大問題 => Exception の存在 Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 25
  • 26. Function的問題点 ○ 関数IF中にExceptionの情報は無い。 proc: A=>C Exceptionは副作用 Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 26
  • 28. Railway Programming Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 28 元ネタ https://fsharpforfunandprofit.com/posts/recipe-part2/ Left(e) Right(b;B)a:A f sealed trait Either[+E, +A]{ … } case class Left[+E](value: E) extends Either[E, Nothing] case class Right[+A](value:A) extends Either[Nothing, A] Optionalの親戚的
  • 29. Railway function Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 29 def railway[A, B, E](f: A => B): A => Either[E, B] = a => { try { if (a == Nothing) { Right(Nothing) } val result = f(a) if (isValid(result) == true) Right(result) else Right(Nothing) } catch(exception e) { Left(e) } }
  • 30. それぞれを Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 30 Left(e) Right(c)b railway(funBtoC) Left(e) Right(b)a railway(funAtoB)
  • 31. 連結できたら良くない? Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 31 Left(e) Right(c)b Left(e) Right(b)a しかし railway(funAtoB) と railway(funBtoC)の入出力の型式が違う railway(funAtoB) railway(funBtoC)
  • 32. ダイアグラム Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 32 A B Either[E, B] Normal Lifted funAtoB railway(funAtoB) C Either[E, C] railway(funBtoC) funBtoC
  • 33. ダイアグラム Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 33 A B Either[E, B] Normal Lifted funAtoB railway(funAtoB) C Either[E, C] railway(funBtoC) funBtoC Either::flatMap
  • 34. flatMap 実装 sealed trait Either[+E, +A]{ def flatMap[EE >: E, B](f : A => Either[EE, B]): Either[EE, B] = this match { case Left(e) => Left(e) // * point case Right(a) => f(a) } Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 34
  • 35. Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 35 Left(e) Right(c)Right(b) Left(e) Right(b)a railway(funAtoB) Either::flatMap(railway(funBtoC)) Left(e)
  • 37. railway(funAtoB) railway(funBtoC)Either::flatMap 一旦 Leftに分岐すると、 何も処理されずスルーされる。 Left(e) Right(c)a *point
  • 38. 例 日常のコード 改 Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 38 def proc_rail[A, C, E](a: A) : Either[E, C] = { Right(a).flatMap(a => railway(funAtoB)(a).flatMap(b => railway(funBtoC)(b))) }
  • 39. 演算子擬似コード Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 39 def (>>=)[A,B,E](eith:Either[E, A])(f:A => Either[E, B]):Either[E, B] = { eith.flatMap(a => f(a)) }
  • 40. Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 40 def proc_rail[A, C, E](a: A) : Either[E, C] = { Right(a) >>= railway(funAtoB) >>= railway(funBtoC) } 演算子>>= を使った擬似コード
  • 41. ダイアグラム Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 41 A B Either[E, B] Normal Lifted railway(funAtoB) C Either[E, C] railway(funBtoC) Either[A, E]
  • 42. 結合則条件 Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 42 Right(a).flatMap(f).flatMap(g) == Right(a).flatMap(aa => f(aa).flatMap(g)) → flatMapの並列結合したものと、 ネストしたflatMapが同値 これと、単位元の存在条件を満たすコンテナクラスの持つ特徴
  • 43. Monad! Monadは、計算の継続を抽象化したもの。 わかりやすい単語表現 “Chainable” flatMap は 別名 bind と言われることもある。 Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 43
  • 44. Either::map def Either::map[A, B](ea:Either[E, A])(f: A => B): Either[E, B] = ea.flatMap(a => Right(f(a))) Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 44
  • 45. さらなる応用 List::map(la:List[A], f:A => Either[E, A]): List[Either[E, A]] List[Right[A], Left[E], Right[A], …] となる場合を考える。 この時、 Listの中にLeftが出てくることを許容できないケースがある とする。 Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 45
  • 46. List[Either[E, A]] が、 Either[E, List[A]] となるようにしたい。 Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 46
  • 47. map2 def Either::map2[E, A, B, C](ea: Either[E, A], eb: Either[E, B], f: (A, B) =>C) : Either[E, C] = ea.flatMap(a => eb.map(b => f(a, b) )) Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 47
  • 48. ちょっと脱線 zip Either::zip[E, A, B](ea: Either[A], eb: Either[E, B]) : Either[E, (A, B)] = Either::map2(ea, eb) ((a, b) => (a, b)) Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 48
  • 49. sequence Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 49 def sequence[E, A](es: List[Either[E, A]]): Either[E, List[A]] = es.foldRight[Either[E,List[A]]](Right(Nil))((head_e, tail_e) => Either::map2(head_e)(tail_e)((head, tail) => head::tail))
  • 50. ダイアグラム sequence Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 50 A ANormal Either[A, E] List[Either[A, E]] Either[List[A], E] List[A, E]
  • 51. まとめ ○ mapのような高階関数はListだけでなく、様々なコンテナ型クラスに実装可能 ○ 同じような計算表現の高階関数を定義できる。 ○ 命令型記述を関数型記述で実現可能となるだけでなく、行間に追加機能を持たせられる。 最上位の表現においては追加機能の詳細記述が隠蔽される。 ○ コンテナ型クラスを多層に出来、さらに層の入れ替えを実現する演算がある。 Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 51
  • 52. 追加情報 ○ 普段使うコンテナ型クラスはmonad性質がある。 → 普段のプログラムは、全てmonad性質を持つコンテナ型クラスで構成できる。 ○ List, Stream (遅延評価) ○ Optional(==Nullable), Either ○ State ○ Async, Parallel (︎↑を基本とし、さらに上位のライブラリを作れる。 例. テストライブラリ(テストデータ自動生成 & データに対する性質テスト) パーサーライブラリ(500行程度でパーサーライブラリ&Jsonパーサ合わせたものがかけます) ○ Reader-monad, Writer-Monad, IO-Monad 等もある ○ Monad(継続 == 命令型的記述)に関連して、関数型言語では、特別な記述様式が用意されている Haskell : do記法, Scala: for記法, F#: computation記法 Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 52
  • 53. 追加情報 ○ unit, flatMap をprimitiveとして、上位Combinatorを実装可能なものが多い。 ○ map, mapN, zip, sequnence, transverse… ○ unit, map2 をprimitiveにすることも可能 primitiveの選択性) ○ コンテナ型クラスのメソッドがある規則を満たせば、それらクラスがさらに抽象的なグループに分別できる 例 Functor 属性 map に対する規則 containr::map[A](c:container, f: A => A) について、 container::map(c, (a => a)) == a が成立するもの ○ 演算に対しても同様にグループ分けもできる。 例 Monoid 属性 結合則 op(op(x, y), z) == op(x, op(y, z)) と単位元 の存在 実例 String , List の加算 => foldable との関係性につながる Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 53
  • 54. 所感 メリット: ○ FPはプログラムを統一型式で表現可能な手段を与えてくれる。 → “統一“というプログラマがコーディング重要指標にあげる点を、徹底的に実現可能。 → 言語を超えた統一表現 ○ FPがもつ真のmodular性 デメリット: 学習コストの高さ FP以外にも、FPと同様の統一型式表現手段があればそれを選択すれば良いですが、恐らくそれも学習コストの高さがあると思います。  結局 2択 皆さんが普段戦っている、プログラムを組む時の大問題”複雑性”に対して * いわゆる普通の命令型の表現を選択して、一つ一つの処理は簡単だが、結果複雑で大きくなり易いコードを書いていく or * 高い学習コストを払ってでも新しい表現を選択し、複雑性を隠蔽できるコードを書いていくか Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL. 54