最近『プログラミングElixir』を読んでいるのですが、パイプ演算子がすごくいい感じだったので Swift でも同じようなことができないかと思って実装してみました。
先に結果を書くと、こういう感じでそれっぽいことができました。
precedencegroup PipePrecedence {
associativity: left
higherThan: AssignmentPrecedence
lowerThan: TernaryPrecedence
}
infix operator |>: PipePrecedence
func |> <Input, Output> (lhs: Input, rhs: (Input) -> Output) -> Output {
return rhs(lhs)
}
// 普通ver
let storyboardName = storyboardName()
let storyboard = UIStoryboard(name: storyboardName, bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: identifier) as? HogeViewController
// パイプ使うver
let viewController = storyboardName()
|> { UIStoryboard(name: $0, bundle: nil) }
|> { $0.instantiateViewController(withIdentifier: identifier) as? HogeViewController }
パイプ演算子って?
『プログラミングElixir』の54ページにあるサンプルコードがすごくわかりやすかったので、コードの部分はそのまま抜粋させていただきます。
普通に書くとこうなるコードがあったとします。
people = DB.find_customers
orders = Orders.for_customers(people)
tax = sales_tax(orders, 2016)
filing = prepare_filing(tax)
iOS アプリを開発する中でも、メソッドの引数として必要な値を事前に一時変数に入れておく、というのはよく見る光景ではないでしょうか。
これを一時変数使わないで書くとこうなります。
filing = prepare_filing(sales_tax(Orders.for_customers(DB.find_customers), 2016))
読みづらいですね。処理順通りに読むためには後ろから読まないといけないし、2016がどの関数の引数なのかわかりづらいです。
パイプ演算子を使うとこう書けます。
filing = DB.find_customers
|> Orders.for_customers
|> sales_tax(2016)
|> prepare_filing
これすごく読みやすいなと思いました。左から処理順に読めるし、最終的に欲しい結果だけが手に入ります。要するにパイプ演算子は「左の式の結果を右の関数に渡す」演算子なのです。そう聞くと演算子と高階関数を自分で作れる Swift なら実装できそうな気がしますね!
Elixir の場合は右の関数の第一引数に左の結果が自動で入るようになっているのですが、この仕様は Swift では実現が難しそうでした。
思ったこと
Swift は演算子を自分で作れますが、やりすぎると自分しか読めないコードになってしまうだろうなと思ってあまり積極的に使おうとは思っていませんでした。でも、他の言語で採用されている演算子なら採用してみるのもおもしろいかもしれません。
『プログラミングElixir』は読んでいる途中ですが、Elixir は Swift を使っていて Ruby もある程度読める、みたいな人にとってはすごく理解しやすいし、いろんな発見があって楽しめる言語だと思いました!