Point-Free Styleとは
関数を渡す関数を使う時にTrailing closureやクロージャ式を使わず関数の識別名を渡すスタイルのことです。
たとえば
func add2(_ i: Int) -> Int {
i + 2
}
という関数を配列内の全てに適用する時に
intArray.map { add2($0) }
// または
intArray.map( { add2($0) } )
と書くかわりに
intArray.map(add2)
と書くスタイルです。
こちらもPoint-Free Styleです。
// 全ての要素を足し合わせる
intArray.reduce(0, +)
引数のメソッドを使いたい
ところが引数のメソッドを使いたい場合通常のやり方ではPoint-Free Styleが使えません。
// 全ての要素が偶数かどうか
intArray.allSatisfy { $0.isMultiple(of: 2) }
むりやり使うための知識
Swiftのメソッドの型
Swiftの関数には型が存在します。たとえば先ほどのadd2
関数の型は(Int) -> Int
です。
func add2(_ i: Int) -> Int {
return i + 2
}
print(type(of: add2)
// prints (Int) -> Int
ではメソッドの型はどうなっているでしょう。
print(type(of: 0.isMultiple))
// print (Int) -> Bool
これも関数と同じ形式の型になっています。
ところがメソッドにはもう一つの取り出し方があります。
print(type(of: Int.isMultiple))
// print (Int) -> (Int) -> Bool
型名
.メソッド名
という形で取り出すことで、インスタンスを引数にとりそのインスタンスがもつメソッドと同形の関数を返す関数として取り出せます。
let method = Int.isMultiple
let methodForOne = method(1) // 1.isMultiple と同じ
print(methodForOne(2)) // 1.isMultiple(of: 2) と同じ
// prints false
// あるいは
print( method(1)(2) )
// prints false
引数の順番を入れ替える
これを使えばPoint-Free Styleを使えそうです。
しかし、引数の順番が逆なのでこれを入れ替える関数が必要です。
func flip<A, B, C>(_ f: @escaping (A) -> (B) -> C) -> (B) -> (A) -> C {
{ b in { a in f(a)(b) } }
}
むりやりPoint-Free Style
準備が整いました。先ほど出した例をむりやりPoint-Free Styleで書いてみましょう。
元の形
// 全ての要素が偶数かどうか
intArray.allSatisfy { $0.isMultiple(of: 2) }
むりやりPoint-Free Style
intArray.allSatisfy(flip(Int.isMultiple)(2))
まとめ
これで引数のメソッドを利用したい時でもPoint-Free Styleで書くことができるようになりました。
ほかにもむりやりPoint-Free Styleはあるはずです。これはPoint-Free Styleは無理とあきらめずに探しだしてみましょう。
変な奴と後ろ指さされること間違いなしです。
追記
問題点があったので追記です。
問題点
引数を持たないメソッドの場合flip
関数が使えない。
以下のような場合です。
// struct Int {
// func signum()
// }
intArray.map(flip(Int.signum)()) // コンパイルエラー
対応
以下のような関数を作ります。
func shrink<A, B>(_ f: @escaping (A) -> () -> B) -> (A) -> B {
{ a in f(a)() }
}
intArray.map(shrink(Int.signum)) // OK