functional programming for all - scala matsuri 2016
TRANSCRIPT
Functional Programming for All
Zachary McCoy
みんなの関数型プログラミング
What is Functional Programming?
• Programming… with functions
• Functions as the main abstraction
• Functions as first class values
「関数を用いたプログラミング」 「関数を主な抽象化の道具とする」「第一級値としての関数」
What is Functional Programming?
• Controlled side effects
• Restricts how we write programs, but not what we can express
「制御された副作用」 「表現の幅は狭めずに、プログラムの書き方を制約する」
Functional Programming• Pure functional core with a layer of side effects on
the outside
• Side effect - an action in addition to return values
• FP - Evaluating expressions
• Imperative - programs are composed of statements
中核は純粋関数でその外側の層で副作用が実行される FP は式を評価するのに対し、命令型は命令文から成る
What is a function?
• An expression involving one or more variables
• Domain and Co-Domain
• Unique mapping from D -> CD
• Immutability, produces something new
関数とは1つもしくは複数の値に関する式 ドメインとコドメインの一意対応、イミュータブル
Pure Functions• No observable side-effects
• Anything that isn’t returning a result
• Mutation
• I/O
• Depends only on arguments or subset of
純粋関数は副作用を持たず、結果は引数にのみ依存する
Pure Functions
• Examples
• Hashing
• Arithmetic
純粋関数例:ハッシュ化、算術演算
Why does purity matter?
• Side effects cause order of evaluation to matter
• Only have to use local reasoning
• Composition and reusability
副作用は実行/評価順の考慮が必要になる、局所化、 合成と再利用性
Why does purity matter?
• Separate the computation over the input from how to obtain it
• Guarantees Referential Transparency
演算と入力を与える方法を分離、参照透過性を保証
Referential Transparency
• An expression can be replaced by its value, provided the expression is pure
• A function can only be RT if the inputs are also RT
• Referential Transparency enables equational reasoning
参照透過性 (RT): 純粋な式がその値と置き換え可能なこと 関数が参照透過であるためには、入力も透過である必要がある
Substitution Model def greaterThan5(i: Int): Option[Int] = if(i > 5) Some(i) else None def createMessage(): String = greaterThan5(3).map(x => "Was greater than 5") getOrElse "Was less than or equal to 5"
def createMessage2(): String = (if(3 > 5) Some(3) else None).map(x => "Was greater than 5") getOrElse "Was less than or equal to 5"
def createMessage3(): String = None.map(x => "Was greater than 5") getOrElse "Was less than or equal to 5"
置き換えモデル
Formalize Referential Transparency
• An expression, E, is said to be referentially transparent if E can be replaced with its value without changing the behavior of a program
• Same effect and output in the end
参照透過性の形式化:式をその値と置き換えることができる プログラムの振る舞いは作用を含め変わってはいけない
Referential Transparency
• Mathematics!
• (2 * 2 = 4)
• Returning errors as values, rather than side effecting
参照透過性は数学! エラーは、副作用ではなく、値で返す
How does this tie together?
• Pure functions enable Referential Transparency
• RT enables the Substitution model and Equational Reasoning
• Pure functions are a huge gain!
純粋関数 ⇒ 参照透過性 ⇒ 置き換えモデル&等式推論 純粋関数 ⇒ ウマー
Scala and FP
• Scala doesn't enforce Referential Transparency
• We have to work for it
• Limit your set of tools: no vars, pulling from out of scope, exceptions
Scala は参照透過性を強制しないため、自前での対応が必要
Scala and FP
• Given an impure function of type A => C we can split it into two functions
• Pure function of A => B, where B is the description of the result
• Impure function of type B => C which is the interpreter of the description
純粋でない関数 A ⇒ C は、純粋関数 A ⇒ B と そのインタプリタ B ⇒ C に分離することが可能
Calculate the oldest
最年長者の計算
case class Person(name: String, age: Int)
val p1 = Person("John", 30) val p2 = Person("Jack", 100)
Calculate the oldest
これはテストするのが難しい 全部副作用で出力されているので参照透過ではない
def calculateOldest(): Unit = { if(p1.age > p2.age) println(s"${p1.name} is oldest") else if(p2.age > p1.age) println(s"${p2.name} is oldest") else println("They are the same age") }
Separation of concerns
関心事の分離
def calculateOldest(p1:Person, p2:Person):Unit = { if(p1.age > p2.age) println(s"${p1.name} is oldest") else if(p2.age > p1.age) println(s"${p2.name} is oldest") else println(s"They are the same age") }
Return values
戻り値を使うことでテストしやすくなった
def calculateOldest(p1: Person, p2: Person): Option[Person] = if(p1.age > p2.age) Some(p1) else if(p2.age > p1.age) Some(p2) else None
We can still split more
さらに細かく分ける
def result(maybePerson:Option[Person]): Unit = maybePerson match { case Some(Person(name, age)) => println(s"${p.name} is oldest") case None => println("They are the same age") }
A pure function core
これでコアが純粋関数になった
def calculateOldest(p1: Person, p2: Person): Option[Person]
def result(maybePerson: Option[Person]): String = maybePerson.map { case Person(name, age) => s"${name} is the oldest" } getOrElse "They are the same age"
def combine(p1: Person, p2: Person): Unit = println(result(calculateOldest(p1,p2)))