Kotlinでletを使うことのメリット


Kotlinの実装を眺めていて疑問があったので質問します。

<https://github.com/JetBrains/kotlin/blob/master/libraries/stdlib/jvm/src/kotlin/text/StringsJVM.kt#L803>

↑のコードの

[code]

    1 -> this[0].let { char -> String(CharArray(n) { char }) }
    
[/code]

は、

[code]

    1 -> String(CharArray(n) { this[0] })
    
[/code]

と書いても同じことのように思えます。単に好みの問題か、それとも`let`を使う書き方には何かメリットがあるのでしょうか。ご教示いただけると幸いです。




まず: `this[0]` というコードは `this` が `CharSequence` であることから
[`CharSequence.get(Int)`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-char-
sequence/get.html) に解決されます。

お示しになられたコードはJVM (Java仮想マシン。以下、JVM) をターゲットにしていると思われるので、以下JVMの話をします。[Java
8](https://docs.oracle.com/javase/jp/8/docs/api/java/lang/CharSequence.html)
では、CharSequenceは以下のように定義されています。

> CharSequenceはchar値の読取り可能なシーケンスです。

[`let`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/let.html)
を用いることで、`let`のレシーバの値を「固定」することができます。すなわち、

[code]

    1 -> this[0].let { char -> String(CharArray(n) { char }) }
    
[/code]

は

[code]

    1 -> run {
        val __tmp = this[0] // 不可視の一時変数
        String(CharArray(n) { __tmp })
    }
    
[/code]

と同じことです ([`run`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/run.html)
はテンポラリなスコープを実現するために使いました) 。

確かに、ご質問者様の言うとおり、`String` (JVMがターゲットですから、`java.lang.String`)
をレシーバにして呼び出された場合、`__tmp` を展開してしまっても変わりありません。`java.lang.String`
はイミュータブルであり、先頭の文字を変更するという芸当はできないため自動的にスレッドセーフになるからです。

しかし、これが
[`StringBuilder`](https://docs.oracle.com/javase/jp/8/docs/api/java/lang/StringBuilder.html)
など、他の`CharSequence`を実装する型だと、「処理中に`this[0]`が変化しない」という前提を敷くことができないため、話が変わってくるものと思われます。実際、その前提は`StringBuilder`を他のスレッドから変更すると崩れてしまいます。




まず: `this[0]` というコードは `this` が `CharSequence` であることから
[`CharSequence.get(Int)`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-char-
sequence/get.html) に解決されます。

お示しになられたコードはJVM (Java仮想マシン。以下、JVM) をターゲットにしていると思われるので、以下JVMの話をします。[Java
8](https://docs.oracle.com/javase/jp/8/docs/api/java/lang/CharSequence.html)
では、CharSequenceは以下のように定義されています。

> CharSequenceはchar値の読取り可能なシーケンスです。

[`let`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/let.html)
を用いることで、`let`のレシーバの値を「固定」することができます。すなわち、

[code]

    1 -> this[0].let { char -> String(CharArray(n) { char }) }
    
[/code]

は

[code]

    1 -> run {
        val __tmp = this[0] // 不可視の一時変数
        String(CharArray(n) { __tmp })
    }
    
[/code]

と同じことです ([`run`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/run.html)
はテンポラリなスコープを実現するために使いました) 。

確かに、ご質問者様の言うとおり、`String` (JVMがターゲットですから、`java.lang.String`)
をレシーバにして呼び出された場合、`__tmp` を展開してしまっても変わりありません。`java.lang.String`
はイミュータブルであり、先頭の文字を変更するという芸当はできないため自動的にスレッドセーフになるからです。

しかし、これが
[`StringBuilder`](https://docs.oracle.com/javase/jp/8/docs/api/java/lang/StringBuilder.html)
など、他の`CharSequence`を実装する型だと、「処理中に`this[0]`が変化しない」という前提を敷くことができないため、話が変わってくるものと思われます。実際、その前提は`StringBuilder`を他のスレッドから変更すると崩れてしまいます。

