Allow strengthening argument types in functions declared for protocol conformance


(Terrence Katzenbaer) #1

This seems to be better addressed with a generic protocol.
I did consider a generic protocols, and it would be good to have some discussion on this vs. my proposed solution.

While there are currently some limitations surrounding generics, and so I won't give specific examples, this has been stated as an area of focus for Swift 3.
Isn't this mailing list's purpose to discuss and propose changes to Swift 3? It seems like you're dismissing this as a "wontfix" with hopes that improving generics would resolve this issue in its entirety.

It seems more expressive and more functionally correct to be able to implement a method strengthening AnyObject to my model type while still conforming to the protocol (since my model would pass "is AnyObject").

This seems to be better addressed with a generic protocol. While there are currently some limitations surrounding generics, and so I won't give specific examples, this has been stated as an area of focus for Swift 3.

If my NSOutlineView contained objects of a single type, it makes little sense to be forced to implement a method solely for handling AnyObjects or being forced to typecast to my model type.

In this example NSOutlineView should be specialized to some type T then.

···

On Dec 16, 2015, at 12:58 PM, ilya <ilya.nikokoshev@gmail.com> wrote:

On Wed, Dec 16, 2015 at 23:45 Terrence Katzenbaer via swift-evolution <swift-evolution@swift.org> wrote:
Maybe I shouldn't have mentioned anything about the runtime since that's more about the implementation instead of the general idea... Something more "swifty" could be a compile check on the newly strengthened arguments.

If I understand your example code, it's calling didPerformSomeAction(object: Bar) instead of the Foo version because of function overloading? It seems this functionally satisfies not having to typecast, but it doesn't solve the verbosity problem since you're still forced to implement the extraneous didPerformSomeAction(object: Foo).

Using the Cocoa framework as an example, NSOutlineViewDelegate has the method:
optional func outlineView(_ outlineView: NSOutlineView,
shouldSelectItem item: AnyObject) -> Bool

If my NSOutlineView contained objects of a single type, it makes little sense to be forced to implement a method solely for handling AnyObjects or being forced to typecast to my model type.

It seems more expressive and more functionally correct to be able to implement a method strengthening AnyObject to my model type while still conforming to the protocol (since my model would pass "is AnyObject").

On Dec 16, 2015, at 12:35 PM, Ian Ynda-Hummel <ianynda@gmail.com> wrote:

Sorry, that should be

extension FooDelegate where Self: BarController \{
    func didPerformSomeAction\(object: Bar\) \{\}
\}

On Wed, Dec 16, 2015 at 3:32 PM Ian Ynda-Hummel <ianynda@gmail.com> wrote:
-1

I think the basic problem is saying the runtime should fail. I feel like a large part of the power of Swift is letting the compiler and the programmer make strong assumptions about types specifically to prevent the runtime from dealing with it.

In fact, the type system can save us here! I wrote some code you can play with here: http://swiftstub.com/318278733

The basic idea is that if you do something like

extension FooDelegate where Self: BarController \{
    func didPerformSomeAction\(object: Foo\) \{\}
\}

calls to didPerformSomeAction with an argument of type Bar will call the right one for the type! There might be better ways to do that, but that is what immediately came to mind.

On Wed, Dec 16, 2015 at 2:43 PM Terrence Katzenbaer via swift-evolution <swift-evolution@swift.org> wrote:
Because APIs are designed to be generic, protocols that must be conformed generally use types like Object or other base classes for a given framework. This introduces type casting verbosity when implementing the protocol for a specific use. I would like to see the ability to strengthen argument types in functions declared for protocol conformance.

An example:
class Foo { }

class Bar: Foo { }

protocol FooDelegate {
func didPerformSomeAction(object: Foo)
}

class FooController: FooDelegate {
func didPerformSomeAction(var object: Foo) {
// I know that object must be a Bar instance
object = object as! Bar
// do something with object
}
}

class ProposedFooController: FooDelegate { // Type 'ProposedFooController' does not conform to protocol 'FooDelegate'
func didPerformSomeAction(object: Bar) {
// do something with Bar instance
}
}

The glaring issue I see outright is how the runtime should fail when `didPerformSomeAction` in `ProposedFooController` is called with a non-Bar instance... But perhaps it /should/ fail outright because the programmer has explicitly stated that the type should be Bar.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Ian Ynda-Hummel) #2

I actually think that generic protocols as they exist in Swift 2 are
expressive enough to do what you want to do.

Let me check my understanding, though. You want a definition of a method of
the same name with a signature whose arguments are at least as specific as
those defined in the protocol to implicitly do the appropriate casting,
correct? So to supply a Swift 2 example, your code would be equivalent to
this:

    class Foo { }

    class Bar: Foo { }

    protocol FooDelegate {
        typealias FooType: Foo
        func didPerformSomeAction(object: FooType)
    }

    class FooController: FooDelegate {
        typealias FooType = Foo
        func didPerformSomeAction(object: Foo) {}
    }

    class ProposedFooController: FooDelegate {
        typealias FooType = Bar
        func didPerformSomeAction(object: Bar) {}
    }

What are the limitations there?

···

On Wed, Dec 16, 2015 at 4:08 PM Terrence Katzenbaer <tkatzenbaer@me.com> wrote:

This seems to be better addressed with a generic protocol.

I did consider a generic protocols, and it would be good to have some
discussion on this vs. my proposed solution.

While there are currently some limitations surrounding generics, and so I
won't give specific examples, this has been stated as an area of focus for
Swift 3.

Isn't this mailing list's purpose to discuss and propose changes to Swift
3? It seems like you're dismissing this as a "wontfix" with hopes that
improving generics would resolve this issue in its entirety.

On Dec 16, 2015, at 12:58 PM, ilya <ilya.nikokoshev@gmail.com> wrote:

> It seems more expressive and more functionally correct to be able
to implement a method strengthening AnyObject to my model type while still
conforming to the protocol (since my model would pass "is AnyObject").

This seems to be better addressed with a generic protocol. While there are
currently some limitations surrounding generics, and so I won't give
specific examples, this has been stated as an area of focus for Swift 3.

> If my NSOutlineView contained objects of a single type, it makes little
sense to be forced to implement a method solely for handling AnyObjects or
being forced to typecast to my model type.

In this example NSOutlineView should be specialized to some type T then.

On Wed, Dec 16, 2015 at 23:45 Terrence Katzenbaer via swift-evolution < > swift-evolution@swift.org> wrote:

Maybe I shouldn't have mentioned anything about the runtime since that's
more about the implementation instead of the general idea... Something more
"swifty" could be a compile check on the newly strengthened arguments.

If I understand your example code, it's calling
didPerformSomeAction(object: Bar) instead of the Foo version because of
function overloading? It seems this functionally satisfies not having to
typecast, but it doesn't solve the verbosity problem since you're still
forced to implement the extraneous didPerformSomeAction(object: Foo).

Using the Cocoa framework as an example, NSOutlineViewDelegate has the
method:
optional func outlineView(_ *outlineView*: NSOutlineView
<https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSOutlineView_Class/index.html#//apple_ref/swift/cl/c:objc(cs)NSOutlineView>
,
         shouldSelectItem *item*: AnyObject
<https://developer.apple.com/library/mac/documentation/Swift/Reference/Swift_AnyObject_Protocol/index.html#//apple_ref/swift/intf/s:PSs9AnyObject>)
-> Bool
<https://developer.apple.com/library/mac/documentation/Swift/Reference/Swift_Bool_Structure/index.html#//apple_ref/swift/struct/s:Sb>

If my NSOutlineView contained objects of a single type, it makes little
sense to be forced to implement a method solely for handling AnyObjects or
being forced to typecast to my model type.

It seems more expressive and more functionally correct to be able
to implement a method strengthening AnyObject to my model type while still
conforming to the protocol (since my model would pass "is AnyObject").

On Dec 16, 2015, at 12:35 PM, Ian Ynda-Hummel <ianynda@gmail.com> wrote:

Sorry, that should be

    extension FooDelegate where Self: BarController {
        func didPerformSomeAction(object: Bar) {}
    }

On Wed, Dec 16, 2015 at 3:32 PM Ian Ynda-Hummel <ianynda@gmail.com> >> wrote:

-1

I think the basic problem is saying the runtime should fail. I feel like
a large part of the power of Swift is letting the compiler and the
programmer make strong assumptions about types specifically to prevent the
runtime from dealing with it.

In fact, the type system can save us here! I wrote some code you can
play with here: http://swiftstub.com/318278733

The basic idea is that if you do something like

    extension FooDelegate where Self: BarController {
        func didPerformSomeAction(object: Foo) {}
    }

calls to didPerformSomeAction with an argument of type Bar will call the
right one for the type! There might be better ways to do that, but that is
what immediately came to mind.

On Wed, Dec 16, 2015 at 2:43 PM Terrence Katzenbaer via swift-evolution < >>> swift-evolution@swift.org> wrote:

Because APIs are designed to be generic, protocols that must be
conformed generally use types like Object or other base classes for a given
framework. This introduces type casting verbosity when implementing the
protocol for a specific use. I would like to see the ability to strengthen
argument types in functions declared for protocol conformance.

An example:

class Foo { }

class Bar: Foo { }

protocol FooDelegate {

    func didPerformSomeAction(object: Foo)

}

class FooController: FooDelegate {

    func didPerformSomeAction(var object: Foo) {

        // I know that object must be a Bar instance

        object = object as! Bar

        // do something with object

    }

}

class ProposedFooController: FooDelegate { // Type
'ProposedFooController' does not conform to protocol 'FooDelegate'

    func didPerformSomeAction(object: Bar) {

        // do something with Bar instance

    }

}

The glaring issue I see outright is how the runtime should fail when
`didPerformSomeAction` in `ProposedFooController` is called with a non-Bar
instance... But perhaps it /should/ fail outright because the programmer
has explicitly stated that the type should be Bar.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________

swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Terrence Katzenbaer) #3

The limitations are that the Cocoa and Cocoa Touch frameworks don't implement this behavior.

···

On Dec 16, 2015, at 1:25 PM, Ian Ynda-Hummel <ianynda@gmail.com> wrote:

I actually think that generic protocols as they exist in Swift 2 are expressive enough to do what you want to do.

Let me check my understanding, though. You want a definition of a method of the same name with a signature whose arguments are at least as specific as those defined in the protocol to implicitly do the appropriate casting, correct? So to supply a Swift 2 example, your code would be equivalent to this:

    class Foo { }

    class Bar: Foo { }

    protocol FooDelegate {
        typealias FooType: Foo
        func didPerformSomeAction(object: FooType)
    }

    class FooController: FooDelegate {
        typealias FooType = Foo
        func didPerformSomeAction(object: Foo) {}
    }

    class ProposedFooController: FooDelegate {
        typealias FooType = Bar
        func didPerformSomeAction(object: Bar) {}
    }

What are the limitations there?

On Wed, Dec 16, 2015 at 4:08 PM Terrence Katzenbaer <tkatzenbaer@me.com> wrote:

This seems to be better addressed with a generic protocol.

I did consider a generic protocols, and it would be good to have some discussion on this vs. my proposed solution.

While there are currently some limitations surrounding generics, and so I won't give specific examples, this has been stated as an area of focus for Swift 3.

Isn't this mailing list's purpose to discuss and propose changes to Swift 3? It seems like you're dismissing this as a "wontfix" with hopes that improving generics would resolve this issue in its entirety.

On Dec 16, 2015, at 12:58 PM, ilya <ilya.nikokoshev@gmail.com> wrote:

> It seems more expressive and more functionally correct to be able to implement a method strengthening AnyObject to my model type while still conforming to the protocol (since my model would pass "is AnyObject").

This seems to be better addressed with a generic protocol. While there are currently some limitations surrounding generics, and so I won't give specific examples, this has been stated as an area of focus for Swift 3.

> If my NSOutlineView contained objects of a single type, it makes little sense to be forced to implement a method solely for handling AnyObjects or being forced to typecast to my model type.

In this example NSOutlineView should be specialized to some type T then.

On Wed, Dec 16, 2015 at 23:45 Terrence Katzenbaer via swift-evolution <swift-evolution@swift.org> wrote:
Maybe I shouldn't have mentioned anything about the runtime since that's more about the implementation instead of the general idea... Something more "swifty" could be a compile check on the newly strengthened arguments.

If I understand your example code, it's calling didPerformSomeAction(object: Bar) instead of the Foo version because of function overloading? It seems this functionally satisfies not having to typecast, but it doesn't solve the verbosity problem since you're still forced to implement the extraneous didPerformSomeAction(object: Foo).

Using the Cocoa framework as an example, NSOutlineViewDelegate has the method:
optional func outlineView(_ outlineView: NSOutlineView,
         shouldSelectItem item: AnyObject) -> Bool

If my NSOutlineView contained objects of a single type, it makes little sense to be forced to implement a method solely for handling AnyObjects or being forced to typecast to my model type.

It seems more expressive and more functionally correct to be able to implement a method strengthening AnyObject to my model type while still conforming to the protocol (since my model would pass "is AnyObject").

On Dec 16, 2015, at 12:35 PM, Ian Ynda-Hummel <ianynda@gmail.com> wrote:

Sorry, that should be

    extension FooDelegate where Self: BarController {
        func didPerformSomeAction(object: Bar) {}
    }

On Wed, Dec 16, 2015 at 3:32 PM Ian Ynda-Hummel <ianynda@gmail.com> wrote:
-1

I think the basic problem is saying the runtime should fail. I feel like a large part of the power of Swift is letting the compiler and the programmer make strong assumptions about types specifically to prevent the runtime from dealing with it.

In fact, the type system can save us here! I wrote some code you can play with here: http://swiftstub.com/318278733

The basic idea is that if you do something like

    extension FooDelegate where Self: BarController {
        func didPerformSomeAction(object: Foo) {}
    }

calls to didPerformSomeAction with an argument of type Bar will call the right one for the type! There might be better ways to do that, but that is what immediately came to mind.

On Wed, Dec 16, 2015 at 2:43 PM Terrence Katzenbaer via swift-evolution <swift-evolution@swift.org> wrote:
Because APIs are designed to be generic, protocols that must be conformed generally use types like Object or other base classes for a given framework. This introduces type casting verbosity when implementing the protocol for a specific use. I would like to see the ability to strengthen argument types in functions declared for protocol conformance.

An example:
class Foo { }

class Bar: Foo { }

protocol FooDelegate {
    func didPerformSomeAction(object: Foo)
}

class FooController: FooDelegate {
    func didPerformSomeAction(var object: Foo) {
        // I know that object must be a Bar instance
        object = object as! Bar
        // do something with object
    }
}

class ProposedFooController: FooDelegate { // Type 'ProposedFooController' does not conform to protocol 'FooDelegate'
    func didPerformSomeAction(object: Bar) {
        // do something with Bar instance
    }
}

The glaring issue I see outright is how the runtime should fail when `didPerformSomeAction` in `ProposedFooController` is called with a non-Bar instance... But perhaps it /should/ fail outright because the programmer has explicitly stated that the type should be Bar.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution