関数型プログラミングにおける副作用の除去は、結果型に副作用の元となるデータを返す
入力Aに対する出力Bを返す関数 f: A => B
は、内部・外部プロセスの変化に影響されない(=副作用がない)
buyCoffeの例では、請求処理という副作用を出力から排除すること
第2章 Scala関数型プログラミングの準備
参照透過性
- 同じ条件を与えれば必ず同じ結果が得られる
- 他のいかなる機能の結果にも影響を与えない
- 関数の結果型に従い、実行する全てのことがその戻り値によって表されるという不変条件
- プログラムによる等式推論(
equational reasoning
)が可能
def add1(x: Int, y: Int): Int = x + y
var x: Int = 1
def add2(y: Int): Int = x + y
参照透過性の確認フロー
例1
val x = "Hello World"
val r1 = x.reverse
// dlroW olleH
val r2 = x.reverse
// dlroW olleH
r1
、r2
は同じx
をx
が参照している式(その定義)と置き換えるval r1 = "Hello World".reverse // dlroW olleH val r2 = "Hello World".reverse // dlroW olleH
r1
、r2
は同じ- 置き換えても結果に影響を与えない
例2
val x = new StringBuilder("Hello")
val y = x.append(" World")
val r1 = y.toString
// Hello World
val r2 = y.toString
// Hello World
r1
、r2
は同じy
をy
が参照している式(その定義)と置き換えるval x = new StringBuilder("Hello") val r1 = x.append(" World"").toString // Hello World val r2 = x.append(" World"").toString // Hello World World
unzip
reduce
List.fill
単相関数(monomorphic function)
- 1つの型のデータだけをを操作する関数
多相関数(polymorphic function)
- 総称関数(generic function)とも呼ばれる
関数リテラル
val f2 = (a: Int)
高階関数パラメータ名
- 引数に関数をとる関数
- 結果型を関数とする関数
- f,g,hのような名称にするのが一般的
末尾再帰のオーバーライド
- サブクラスで末尾再帰のメソッドをオーバーライドしようとするとコンパイルエラー
- private final をつける http://www.ne.jp/asahi/hishidama/home/tech/scala/annotation/tailrec.html
関数型データ構造の定義
- データコンストラクタ
反変(contravariant)
- 狭い型から、広い型へ変換
第3章 関数型プログラミングのデータ構造
代数的データ型
- algebraic data type
- 1つ以上のデータコンストラクタによって定義されるデータ型
- コンストラクタは0個以上の引数を受け取る
- データ型がそのデータコンストラクタの和である