問題
def superCalculate(): Int = 42
def hyperCalculate(i: Int): Int = i * i
def formatCutely(i: Int): String = "♡♡♡♡♡ " + i + " ♡♡♡♡♡"
Some(formatCutely(hyperCalculate(superCalculate())))
↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
ここが非常にわかりづらい。どういう意味でわかりづらいかというと処理順番と表記順番が逆である点だ。
処理順番は
- superCalculate
- hyperCalculate
- formatCutely
- Some
であるにも関わらず、コードを読むときは
- Some
- formatCutely
- hyperCalculate
- superCalculate
の順番で読むことになる。これはコーダーの思考に余計な負荷を掛け、設計や名前などのより重要な要素へ割く注意力を削る。またコードを読む方にとっても非常に理解しづらくなる。
この読みづらさを緩和するため、以下のように中間変数への割り当てを行うこともよくある。
val superResult = superCalculate()
val hyperResult = hyperCalculate(superResult)
val formattedString = formatCutely(hyperResult)
Some(formattedString)
しかしこのテクニックも問題が多い。
- 無駄に変数が増えることでスコープ内からアクセスできる変数が増えてコードの複雑性が増す
- ごく短期間しか使わない変数のために適切な名前をつける必要がある
- 行数が増えて見かけの複雑性が増加する
解決方法
scala 2.13で追加される(予定の)pipeメソッド1を使う。
これを使えば先程のコードがこう書ける。
import scala.util.chaining._
superCalculate().pipe(hyperCalculate).pipe(formatCutely).pipe(Some.apply)
これにより処理順番と表記順番を一致させることができた。
※ scala 2.12なら拙作ながら2.13のpipe実装をバックポートしたライブラリがあるのでこちらを使う(bigwheel/util-backports)。
蛇足
他の言語ではこのpipeの役割を果たす関数に|>
のシンボル演算子を割り当てることが多いが、scalaではシンボル演算子を公式に割り当てることはしないと判断されている。詳細は1のPR参照。