protocol-scoped methods


(Benjamin Spratling) #1

Howdy,

Several months ago, there were a few well-received ideas on the list of adding functions which would have static-like dispatch directly scoped to protocols, instead of only to types conforming to those protocols.
From a review of the mailing list, it looks like these ideas got lost amongst some others, and the goal at the time was “cut & fix”, not “add”.
I’m concerned these may require ABI changes, and I’ve noticed their absence negatively affects my code designs, so I’m bring them back up.

The first, and maybe most obvious is protocol-scoped functions.

Right now, static functions require a conforming type, even when default implementations exist.

protocol Usable {
  static func use()
}

extension Usable {
  static func use() { }
}

struct User : Usable {
}

User.use() //compiles
type(of:User()).use() //compiles

struct AnotherUser : Usable {
  static func use() { }
}

AnotherUser.use() //compiles & calls AnotherUser’s implementation instead of protocol default

While that is useful, and I’m not suggesting removing it, there is room for another kind of static-like method, one which does not require knowledge of an instance of a conforming type, nor of a conforming type. A protocol-scoped method

protocol Usable {
}

extension Usable {
  protocol func use() { }
}

Usable.use() //if accepted, this would compile end execute.

Right now, “static” methods must have a supported type.

Without this feature:
1. many methods become global, which feels very inappropriate in Swift
2. To get nice inits for “Struct clusters”, they become become enums, which does not leave them open for extension, the opposite of the intention of protocols.

With this feature:
Common pre-validation or -pre-instantiation logic would be available to code which works directly with protocols , instead of only code in another module which knows about concrete types of it.
The function is not included in the original protocol definition, because it is not something which must be implemented by the conforming type.

A derivative concept is protocol-scoped stored vars/lets. Like static vars & let, protocol-scoped vars & lets have static storage, but are name spaced by the protocol, instead of getting dumped into the global name space.

As an example, I have a protocol which creates URLRequests for data serialized as JSON. I’d like to have a protocol-scoped “jsonMimeType” var for insertion in my algorithm, but that is not currently allowed, at least not using a good name-space. In fact, it’s not even allowed as a static var/let right now, at least with a default implementation. And since this is something that shouldn’t be overridden, it doesn’t make sense in a static, who’s default implementation could be replaced by a conforming type’s implementation.

Protocol Nested Types.
I understand there’s a lot of work to do to support nested generics, but surely nested non-generics are pretty straight forward?

For instance, I have a protocol which requires instances to implement a method which returns a case of an enum which is peculiar to that protocol. I may very well wish to make the enum a nested type of the protocol. This feels like a natural extension of the language.

Two of the emails mentioned the desire for “existential protocols”, but in an hour of searching, I did not find an “existential protocol manifesto”, so I don’t know.

Questions:
1. Have I missed something, and these features are already supported in Swift?
2. Would these features benefit your use of Swift?
3. Would these features affect the ABI?
4. Are these features part of a larger initiative already considered? (links please)

-Ben Spratling


(Goffredo Marocchi) #2

Honestly I am a bit concerned with protocols getting more and more code in them, default methods included.

Protocols for me are best to abstract away, to decouple api behaviour from implementation... which also makes the system a lot easier to mock.

···

Sent from my iPhone

On 2 Oct 2016, at 10:42, Benjamin Spratling via swift-evolution <swift-evolution@swift.org> wrote:

Howdy,

Several months ago, there were a few well-received ideas on the list of adding functions which would have static-like dispatch directly scoped to protocols, instead of only to types conforming to those protocols.
From a review of the mailing list, it looks like these ideas got lost amongst some others, and the goal at the time was “cut & fix”, not “add”.
I’m concerned these may require ABI changes, and I’ve noticed their absence negatively affects my code designs, so I’m bring them back up.

The first, and maybe most obvious is protocol-scoped functions.

Right now, static functions require a conforming type, even when default implementations exist.

protocol Usable {
   static func use()
}

extension Usable {
   static func use() { }
}

struct User : Usable {
}

User.use() //compiles
type(of:User()).use() //compiles

struct AnotherUser : Usable {
   static func use() { }
}

AnotherUser.use() //compiles & calls AnotherUser’s implementation instead of protocol default

While that is useful, and I’m not suggesting removing it, there is room for another kind of static-like method, one which does not require knowledge of an instance of a conforming type, nor of a conforming type. A protocol-scoped method

protocol Usable {
}

extension Usable {
   protocol func use() { }
}

Usable.use() //if accepted, this would compile end execute.

Right now, “static” methods must have a supported type.

Without this feature:
1. many methods become global, which feels very inappropriate in Swift
2. To get nice inits for “Struct clusters”, they become become enums, which does not leave them open for extension, the opposite of the intention of protocols.

With this feature:
Common pre-validation or -pre-instantiation logic would be available to code which works directly with protocols , instead of only code in another module which knows about concrete types of it.
The function is not included in the original protocol definition, because it is not something which must be implemented by the conforming type.

A derivative concept is protocol-scoped stored vars/lets. Like static vars & let, protocol-scoped vars & lets have static storage, but are name spaced by the protocol, instead of getting dumped into the global name space.

As an example, I have a protocol which creates URLRequests for data serialized as JSON. I’d like to have a protocol-scoped “jsonMimeType” var for insertion in my algorithm, but that is not currently allowed, at least not using a good name-space. In fact, it’s not even allowed as a static var/let right now, at least with a default implementation. And since this is something that shouldn’t be overridden, it doesn’t make sense in a static, who’s default implementation could be replaced by a conforming type’s implementation.

Protocol Nested Types.
I understand there’s a lot of work to do to support nested generics, but surely nested non-generics are pretty straight forward?

For instance, I have a protocol which requires instances to implement a method which returns a case of an enum which is peculiar to that protocol. I may very well wish to make the enum a nested type of the protocol. This feels like a natural extension of the language.

Two of the emails mentioned the desire for “existential protocols”, but in an hour of searching, I did not find an “existential protocol manifesto”, so I don’t know.

Questions:
1. Have I missed something, and these features are already supported in Swift?
2. Would these features benefit your use of Swift?
3. Would these features affect the ABI?
4. Are these features part of a larger initiative already considered? (links please)

-Ben Spratling

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