部分関数のことだよ
そもそも関数ってなあに?
一般にプログラミングでいう関数は全域関数(total function)のことだよ
値を渡したら必ず値を返してくれる関数のことだよ
部分関数は全域関数と違って、値を渡しても値を返してくれないことがあるよ
- 例外を投げる関数
- 停止しない関数
- 失敗する関数
- 一部しか処理する気のない関数
数学的には部分関数だけど Scala の PartialFunction ではないよ
これも数学的には部分関数だけど Scala の PartialFunction ではないよ
例えば A => Option[B] のようなもの
unlift メソッドとか見てわかって
これは Scala がリテラルから生成する PartialFunction に一番近いよ (注:言語仕様的には PartialFunction のためのリテラルなんてないです)
collect メソッドに渡してる部分がそうだよ
val oddStrings = (1 to 10).collect {
case x if x % 2 == 1 => x.toString
}
A => Option[B] と collect に渡されたものは、ちょっと違うよ
その前に Scala における PartialFunction と Function1 の違いをちゃんと知ろう
abstarct class PartialFunction[-A, +B] extends (A => B) {
def isDefinedAt(x: A): Boolean
}
極論すると、これだけだよ
関数(Function1)が引数値を処理できるかどうかを知るためのメソッドだよ。
A => Option[B] の場合は、実際に関数を評価しないといけないよ
A => Option[Lazy[B]] とか A => LazyOption[B] とかなら…
Scala コンパイラがうまいこと定義してくれるから、パターン部しか評価されないよ
val oddStrings = (1 to 10).collect {
case x if x % 2 == 1 => x.toString
}
x.toString は評価されないよ
値を処理できるかどうか「だけ」を知りたいときに、計算を保留してもらえるよ
Akka の Actor とかがそうだよ
// 簡略化してます、実際はもっと色々定義してあります
trait Actor {
def receive: PartialFunction[Any, Unit]
}
こんな風に使うよ
class GreetingActor extends Actor with ActorLogging {
def receive = {
case Greeting(who) ⇒ log.info("Hello " + who)
}
}
GreetingActor は処理できるメッセージ(Greeting)だけを処理するよ
所謂メッセージ指向だよ、分散処理とか得意だよ
じゃあ聞くけど Function は何が嬉しいの?
関数型プログラミングなんて JavaN(N < 8)でもできるよね?
e.g. Functional Java
でもリテラルがないと書くのも読むのも冗長になっちゃう
出自忘れた
- 関数が first-class value であること
- 関数を記述するリテラルがあること
二番目が重要!
いわせんなよ恥ずかしい or それを記述するには以下略
言葉も判断基準も今ボクが考えた(!)
- 部分関数が first-class value であること
- 部分関数を記述するリテラルがあること
関数を言語としてサポートする利点と同じだよ
そのパラダイムでのプログラミングの記述性、可読性が向上する!
メッセージ指向、イベント駆動型のプログラミングの実装に向いてるよ(Akka とか)
普通の関数より小さい計算を合成して大きい関数を作れるよ
compose メソッドを使ったことは?
flatMap メソッドを使ったことは?
型よりもっと細かい単位でそれらのメソッドを使いたいと思ったことは?
残念(口頭でカバーしたい)
- PartialFunction は便利だよ
- ある領域の問題を解決する時は特に便利だよ
- 言語サポートがあると記述性、可読性が向上するので嬉しいね
!SLIDE 以後のページは発表時にはなかった追記になります
動的型付き言語で、大きすぎる関数を分割するとき、型と値、どちらを意識しますか?
値について意識しながら分割することもあるのではないでしょうか
- 通常の関数では型のレベルでしか計算を分割できない
- 嘘。正確には勿論できるけれど、その事実は型に現れてこない
- 部分関数なら、型より小さいレベル、値レベルで計算を分割できる
- これ とかで雰囲気伝わらないかなあ
- 部分関数なら分岐処理も含めて合成できるので、余計な分岐を書かずにすむ
- 通常の関数だと分岐処理は書かざるを得ない
- 実は open recursion で部分関数の合成相当の処理は実現できるけれど、ここまで綺麗にはできない
- 合成、合成といっていますが、PartialFunction#orElse は horizontal な合成です
- 一方 Function1#compose は vertical な合成です !SLIDE 追記おわり
網羅性には気をつけてね!