1

In the old swift world (2.0 I believe) I had the following Y-combinator implementation

func Y<T, R>( f: (T -> R) -> (T -> R) ) -> (T -> R) {
    return { (t: T) -> R in
        return f(self.Y(f))(t)
    }
}

I would call the Y-comb elsewhere to create a recursive closure, like so:

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()

The idea being that as the observer of the 'sendData' notification received his responses, he'd send a notification to class encompassing my Y-combinator and the repeating closure. Once all the instances of the observer of the 'sendData' notification have received their data,

self.responses_received == responses_expected

would be true, and we wouldn't call f() again.

Now, my issue is that the conversion to Swift 3.0 has forced me to explicitly declare the type of 'f' to be @escaping, on the definition of Y like so:

func Y<T, R>( _ f: @escaping ((T) -> R) -> ((T) -> R) ) -> ((T) -> R) {
    return { (t: T) -> R in
        return f(self.Y(f))(t)
    }
}

and subsequently converted my repeating closure to have the same type. Which is fine, I understand the difference between @escaping and @noescape and why my code needs it. However, when attempting to build I get a compile error about the types not matching:

Cannot convert value of type '(@escaping () -> ()) -> (() -> ())' 
to expected argument type '(() -> ()) -> (() -> ())'

I've created a simple example closure just to test with which gives the same error:

let innerClosure = { (f: @escaping ()->()) -> (()->()) in
    return {}
}

let repeatClosure = self.Y(innerClosure)

Whereas the following has no issue:

let innerClosure = { (f: ()->()) -> (()->()) in
    return {}
}

let repeatClosure = self.Y(innerClosure)

I get the suggestion from fixit to force a cast to the type, sans the @escaping tag. This compiles, but it feels wrong and I haven't actually tested whether that cast will actually work at run time.

What am I missing here?

agreendev
  • 169
  • 2
  • 8

2 Answers2

2

Your Y function should have the following signature:

func Y<T, R>(_ f: @escaping (@escaping (T) -> R) -> ((T) -> R)) -> ((T) -> R)

since it accepts an escaping function which itself needs an escaping function.

When you call Y, the closure should start with:

self.Y { (f: @escaping () -> ()) -> (() -> ()) in

notice that this @escaping corresponds to the second escaping in the Y signature. It was a mismatch on this @escaping that was causing your compiler error.

Most of the rest should sort itself out (once you update the Dispatch and NSNotificationCenter calls to Swift 3).

Matt Gallagher
  • 14,560
  • 2
  • 39
  • 42
0

I have this code in a playground and it builds without any issue:

class Foo {
  func Y<T,R>(_ f: @escaping ((T) -> R) -> ((T) -> R) ) -> ((T) -> R) {
    return { (t: T) -> R in
      return f(self.Y(f))(t)
    }
  }

  func test() {
    let innerClosure = { (f: ()->()) -> (()->()) in
      print("test")
      return {}
    }

    let repeatClosure = self.Y(innerClosure)
    repeatClosure()
  }
}

Foo().test() // prints "test"

You might want to clean your build and re-try building because I don't get that fixit suggestion in my playground.