[swift-evolution] Swift should support generics as arguments to closures in any context

Spencer Kohan spencerkohan at gmail.com
Fri Jan 15 16:14:46 CST 2016


If I understand correctly, this still doesn't solve the problem exactly.
Wouldn't Self in this context refer to the class that implements MyProtocol?

The example I raised is just a single use-case I ran into, but I think the
more general point is that there isn't a good way to tell the compiler that
a closure parameter must belong to more than one protocol, or a type plus a
protocol.

Here's another (somewhat contrived) example:

Say I was writing an application like IMDB, and I had a protocol for
directors and another for writers:

    protocol Director {
        func directingCredits() -> [Film]
    }

    protocol Writer {
        func writingCredits() -> [Film]
    }

And I want to have a closure which returns all the movies written and
directed by the same person:

    var getWrittenAndDirectedFilms : (<Writer, Director>)->[Film] = {
writerDirector in
        return  writerDirector.writingCredits().filter {
writerDirector.directingCredits().contains($0) }
    }

Not all writers are directors, and not all directors are writers, but it's
perfectly reasonable to have an operation which only works on objects which
conform to those two protocols.

It's easy to write a plain function that handles this use case using
generics, but it's not possible with a closure.  It seems like an
unnecessary limitation.

On Fri, Jan 15, 2016 at 6:01 PM, Félix Cloutier <felixcca at yahoo.ca> wrote:

> Are you sure that your problem is about generics on lambdas? Seems to me
> that you wouldn't need generic lambdas if you could solve your initial
> problem in a different way.
>
> You can use Self in protocols, which refers to the implementing type:
>
> protocol MyProtocol {
> var protocolControllerCallback: Self -> Void { get }
> }
>
> class MyController: MyProtocol {
> var protocolControllerCallback: MyController -> Void
> init() {
> protocolControllerCallback = { cnt in return }
> }
> }
>
>
> That doesn't work right now because Swift will complain that the class
> must be `final` for reasons that sound bogus to me: "it uses 'Self' in a
> non-parameter, non-result type position". I don't know if there's a
> technical reason that it can't be in a closure parameter. Still seems to
> get you closer to what you want.
>
> Félix
>
> Le 15 janv. 2016 à 05:07:06, Spencer Kohan via swift-evolution <
> swift-evolution at swift.org> a écrit :
>
> New here, hope I'm doing this right.
>
> I recently found a use case which does not appear to be covered by the
> swift language: it appears that there's no way to declare a closure type
> with generic arguments.
>
> For example, what if I'm building an iOS app, and I want to define a
> closure which operates on UIViewControllers which implement a particular
> protocol?
>
> In ObjectiveC, this would be possible with the following syntax:
>
>     typedef void (^MyBlockType)(UIViewController<MyProtocol>)
>
> In Swift you can do this in the context of a function, class or struct:
>
>     func myFunction<T: UIViewController where T:MyProtocol>() {
>         let x : (T) -> () = { generic in
>            ....
>         }
>     }
>
> But what if you want to declare such a closure as a variable within the
> protocol it's referring to?
>
>     protocol MyProtocol {
>         var protocolControllerCallback : (protocolController : ???? )->()
>     }
>
> It seems like Associated Types are the intended tool for this job, so you
> could have something like:
>
>     protocol MyProtocol {
>         typealias ProtocolObservingController
>         var protocolControllerCallback : (protocolController
> : ProtocolObservingController )->() {get set}
>     }
>
>     class MyController : UIViewController, MyProtocol {
>         typealias ProtocolObservingController = MyController
>         ...
>     }
>
> But this doesn't quite solve the problem: we still can't have a closure
> defined in this protocol which can operate on *any* implementations of the
> protocol.  For instance, what if we wanted to pass a closure between two
> different implementations?
>
>     class MyChildController : UIViewController, MyProtocol {
>         typealias ProtocolObservingController = MyChildController
>         ...
>     }
>
>     class MyParentController : UIViewController, MyProtocol {
>         typealias ProtocolObservingController = MyParentController
>         ...
>
>         func createChildController() -> MyControllerChild {
>             let child = MyChildController()
>             child.protocolControllerCallback =
> self.protocolControllerCallback
>         }
>
>     }
>
> It doesn't work because the types of protocolControllerCallback are
> inconsistent between the two classes.
>
> This feels like a hole in the language.
>
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>


-- 
Spencer Kohan
cell: 216 288 8258
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160115/481c1844/attachment.html>


More information about the swift-evolution mailing list