0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

参照透過性ってなんだっけ

Posted at

参照透過性ってなんだっけ?

ふと「参照透過性」てなんだっけ?と気になったので、色々考えたことをまとめてみる。

参照透過性を調べると「式をその値に置き換えてもプログラムの意味が変わらないこと」と出てくる。
参照透過性がある例はこんな感じ。

fun add(x: Int, y: Int): Int {
    return x + y
}

val sum = add(2, 3)
// これはadd(2, 3)の戻り値5に置き換えても意味は同じ
val sum = 5

そんなに特別なことじゃなくない?と思ったけど、参照透過性を満たさない例は普通にある。
例えば標準出力。

val x = println("Hello")

Kotlinのprintlnの戻り値はUnitだけど、

val x = Unit

とは明らかに意味が異なる。

他にも、内部の状態を変更するような関数は参照透過性を持たない。

class Counter() {
    var i = 0
    
    fun increment() {
        i++
        return i
    }
}

この increment() という関数は

val counter = Counter()
val x = counter.increment() // x = 1
val y = counter.increment() // y = 2

だが、

val counter = Counter()
val x = 1 // 右辺を値に置き換えると
val y = counter.increment() // yの値が1に変わった!

明らかに意味が変わってしまう。

「副作用」が関係するらしい

関数の戻り値を返す以外の、状態を変化させるような操作を言う。
例えば、グローバル変数の値を書き換える、DBやファイルへの永続化、標準出力などが挙げられる。
また、関数の外部の状態を受け取ることも副作用と呼ぶようで、DBやファイルの読み取りも副作用である。また、乱数や現在時刻など、実行結果が都度変わってしまうようなものも副作用に含まれる。

副作用を持つような関数は参照透過にはならない。
参照透過性と副作用の概念は微妙に異なるはずだが、よくわからない。

冪等性とは違うんだっけ?

冪等性とは、同じ処理を何度繰り返しても結果や状態が変化しないこと。
HTTPメソッドのPUTPATCHの違いとかで有名。

参照透過性を持つ関数は当然冪等性を持つ。

val x = someFunction()
val y = someFunction()
//もし x!=y ならば sumFunctionは副作用を持っている。

ただし、冪等性を持つ関数が参照透過性を持つとは限らない。

hoge.setValue(42)
hoge.setValue(42)
hoge.saeValue(42)
... // 何度繰り返しても hoge.value == 42

だが、hogeの内部の状態を変更しており、参照透過性はない。

そもそも戻り値がUnitの関数は参照透過性を持ち得ない

println()もsetterも、戻り値がUnitである。
このような(実質)何も返さない関数は、何かしらの副作用を持つ処理を行っているはずで、そうなると参照透過性は持たない。
厳密には

fun doNothing() {}

という関数は参照透過性を持つが、何もしない関数を呼び出すことに通常意味はないので、戻り値がUnitの関数は参照透過性を持たないと考えてよい。

オブジェクト指向のメソッドは?

ところで、Kotlinはオブジェクト指向の言語である。クラスに紐づくメソッドの扱いはどうなるんだろうか?
例えば、単純なgetterを見てみると

hoge.getValue() // 仮に111になるとする
hoge.setValue(222) 
hoge.getValue() // ここでは222

hoge.getValue()の値が変わっているので、参照透過性を持たないということになるのだろうか...?

さらに、クラスを引数に持つ場合はどうなるか?

class Point(val x, val y)

こういうクラスを引数に持つような関数

distanceFromOrigin(p)

を考える。これはどうなるか?
PointというクラスをValue Objectとして設計しておけば、参照透過性を持つと言っていいきがす。

そもそも、オブジェクト指向は「(隠蔽された)内部状態を持つクラスの振る舞い」を基に記述するし、対して関数型プログラミングは「(数学の関数のように)同じ引数に対して常に同じ戻り値を返す関数」を中心とするというような違いがある。
思想が異なるものを無理に当てはめようとしているので、わかりづらくなっている面がありそう。

まとめ

「参照透過性」として「式が値に置き換えられる」と言う意味は大きく分けて2つありそう。
1つ目が、「戻り値の値以外、関数が意味のある処理を行なっていない」こと。
2つ目が、「同じ引数に対して、必ず(外部の状態などに依存せず)同じ値を返す」こと。

副作用とか純関数などの概念と関わりがあるので、きちんと改めて関数型プログラミングを勉強してみようと思った。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?