古い迅速な世界(2.0だと思います)では、次のYコンビネーターの実装がありました
func Y<T, R>( f: (T -> R) -> (T -> R) ) -> (T -> R) {
return { (t: T) -> R in
return f(self.Y(f))(t)
}
}
Y-comb を別の場所で呼び出して、次のように再帰クロージャーを作成します。
let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC)))
let repeatClosure = self.Y {
(f: () -> () ) -> (() -> ()) in
return {
if self.responses_received != responses_expected {
dispatch_after(delayTime, dispatch_get_main_queue()) {
// Resend
NSNotificationCenter.defaultCenter().
postNotificationName(sendData,
object: nil, userInfo: dataToSend)
f()
}
} else {
print("Completed!")
self.responses_received = 0
}
}
}
repeatClosure()
「sendData」通知のオブザーバーが応答を受信すると、Y コンビネーターと反復クロージャーを含むクラスに通知を送信するという考えです。「sendData」通知のオブザーバーのすべてのインスタンスがデータを受信すると、
self.responses_received == responses_expected
f() を再度呼び出すことはありません。
さて、私の問題は、Swift 3.0 への変換により、次のように Y の定義で、「f」の型を @escaping であると明示的に宣言することを余儀なくされたことです。
func Y<T, R>( _ f: @escaping ((T) -> R) -> ((T) -> R) ) -> ((T) -> R) {
return { (t: T) -> R in
return f(self.Y(f))(t)
}
}
その後、繰り返しクロージャーを同じタイプに変換しました。@escaping と @noescape の違いと、コードがそれを必要とする理由を理解しています。ただし、ビルドしようとすると、型が一致しないというコンパイル エラーが発生します。
Cannot convert value of type '(@escaping () -> ()) -> (() -> ())'
to expected argument type '(() -> ()) -> (() -> ())'
テストするためだけに、同じエラーが発生する簡単なクロージャーの例を作成しました。
let innerClosure = { (f: @escaping ()->()) -> (()->()) in
return {}
}
let repeatClosure = self.Y(innerClosure)
以下は問題ありませんが:
let innerClosure = { (f: ()->()) -> (()->()) in
return {}
}
let repeatClosure = self.Y(innerClosure)
@escaping タグを使わずに、型へのキャストを強制するよう fixit から提案を受けました。これはコンパイルされますが、間違っているように感じます。そのキャストが実行時に実際に機能するかどうかを実際にテストしていません。
ここで何が欠けていますか?