はじめに
はじめましての方ははじめまして。 Scala エンジニアの Javakky です!
今回は、 Scala の標準ライブラリにある pipe / tap というメソッドを求めて旅した過程を書いていこうと思います。
pipe / tap とは?
Scala Standard Library 2.13.6 - scala.util.ChainingOpsで定義されているメソッドで、任意の値をメソッドチェーンのソースとして扱えるすごいやつです。
pipe
pipe メソッドは任意の値を受け取り、処理結果を返す関数に値を渡してくれるメソッドです。
今回求めていた機能もこいつになります。
/** Converts the value by applying the function f. */ def pipe[B](f: (A) => B): B // example val i = (1 - 2 - 3) .pipe((_: Int) * 6) .pipe(scala.math.abs) // i: Int = 24
tap
また、同じクラスで定義されている tap メソッドでは、元の値に任意の処理をしつつ、値をそのまま返すことができます。
戻り値のない処理をメソッドチェーンの中間でやりたくなった時に便利です。
/** Applies f to the value for its side effects, and returns the original value. */ def tap[U](f: (A) => U): A // example val xs = List(1, 2, 3) .tap(ys => println("debug " + ys.toString)) // debug List(1, 2, 3) // xs: List[Int] = List(1, 2, 3)
発端
前の返り値を同じ名前で再束縛してメソッドチェーンするみたいな書き方がしたいが微妙に綺麗な書き方を思いつかなくて困っている#Scala
— Javakky //流石にそろそろ働く (@Javakky_P) 2022年10月7日
イミュータブルな処理を手続き的に書く際に、1処理ごとに変数の命名を行うのは面倒くさいな〜でも時々は前処理の結果を複数回使いたいな〜と考えていました。
やりたい処理はこんな感じ ↓
var target = init()
target = target.hoge()
target = target.huga()
target = target.piyo()
target
これは、メソッドチェーンで以下のように書くことができます。
init().hoge().huga().piyo()
しかし、以下のようなコードではどうでしょうか?
var target = init() target = target.hoge() target = target.huga() match { case A => A case _ => target } target = target.piyo() target
一応 match
を利用して変数を再定義してやれば書くことができました。
var target = init() target.hoge() match { case target => target.huga() match { case A => A case _ => target } match { case target => target.piyo() } }
しかし、これだと手続きの行数が増えれば増えるほど、ネストがどんどん深くなってしまいます。
Implicit Conversion で自作しよう
ここで任意の型 A をメソッドチェーンに変換することができればやりたいことができるのでは?と思いつきました。
implicit class UsePrevious[A](a: A) { def use[B](f: A => B): B = f(a) }
これがあれば以下のように書くことができるので、1段分のネストは生まれてしまうものの、全ての処理を縦に並べて記述することができました。
init() .hoge() .use { target => target.huga() match { case A => A case _ => target } }.piyo()
終幕
はい。無事全く同じシグネチャのメソッドが Scala の標準ライブラリにありました。
リプライをくださった皆さん、ありがとうございました。
pipe/tap を使うところかと。 https://t.co/xt2kXMgzu1
— Yasuo Nakanishi (@nakanishiyasuo) 2022年10月7日
みなさんもぜひ色々な標準ライブラリを触ってみてください。