Scalaモード:パイプオペレータ

2628 ワード

に質問
私たちがコードを書くのは左から右へ書くのが好きで、どんなに流暢で、どんなに順調ですか.
このコードを考慮すると、いくつかのデータのjson形式を返します.多分こう書きます.
def getyou() = {
  Json(Map("a"->1, "b"->2))
}

私はデータを取得する式Map("a"->1, "b"->2)を書いて、jsonに戻ることを思い出して、そこで行頭に移動して、Json(と書いて、更に行尾に移動して、)と書きます.
左から右へ書くことができなくて、流暢ではありません!
データオブジェクトにtoJsonメソッドがあれば、左から右にdata.toJsonと直接書くことができ、スムーズです!しかし、一般的な相手にtoJsonメソッドがない場合はどうしますか?
単純配管
昨夜UNIX shellのパイプ:a | b | cを思い出しました.もしScalaにこの文法が内蔵されていたらどんなにいいだろう.
実は、ScalaのDSL能力を利用して、自分で実現することもできます.コードを見てください:
object Json {
  def |:(obj: Map[_, _]) = {
    val conc = obj.map{p => "  " + p._1 + ": " + p._2}.mkString(",
") "{
" + conc + "
}" } } Map("a"->1, "b"->2) |: Json

カスタマイズされた|:オペレータ.Scalaでは、メソッド名として記号を使用することができます.オペレータのように、このメソッドのthisパラメータが右側にあることを示すコロン接尾辞が使用されます.実際にコンパイラによってJson.|:(Map("a"->1, "b"->2))に翻訳され、左から右へスムーズにコードが書けるようになりました
残念なことに、複数のパイプを簡単に接続することはできません.data|:Json|:Printlnはできません.このオペレータは右結合されているので、呼び出し順序は左から左で、左から右ではありません.かっこを付けない限り:
object Println {
  def |:(obj: Any) = println(obj)
}

(Map("a"->1, "b"->2) |: Json) |: Println

行頭にかっこをつけるのは避けられません.流暢ではありません.
れんぞくかんろ
方法はまだありますが、正常な左結合オペレータをなんとか運用します.任意のオブジェクトを拡張できればいいです.では、任意のオブジェクトにパイプオペレータを拡張しましょう.究極の実装コード:
implicit def pipify[T](t: T) = new {
  def |[R](f: T=>R): R = f(t)
}

def json(obj: Map[_, _]) = obj |: Json

Map("a"->1, "b"->2) | json | println

実はコンパイラに翻訳されてpipify(pipify(Map("a"->1, "b"->2)).|(json)).|(println)ハハ、気絶しましたか?
完全な例
フルコードはこちらで提供しておりますので、REPLまたはIDEのScala WorkSheetで実行してください.
val data = Map("a"->1, "b"->2)

object Json {
  def |:(obj: Map[_, _]) = {
    val conc = obj.map{p => "  " + p._1 + ": " + p._2}.mkString(",
") "{
" + conc + "
}" } } def json(obj: Map[_, _]) = obj |: Json object Println { def |:(obj: Any) = println(obj) } implicit def piping[T](t: T) = new { def |[R](f: T=>R): R = f(t) } (data |: Json) |: Println data | json | println

(Update:|>と書かれている流派を発見し、とにかく使用者自選)
Rubyのような動的言語も似たように拡張できますが、こんなに簡潔にはできないかもしれません.大体次のように書くことができます.
#a        
#    
a.pipe b
#    
a.pipe(b).pipe(c)

誰かRubyでもっと簡潔に実現できたら教えてください!