[Proposal] Allow static protocol extension methods to be called directly on the protocol

I propose to allow static protocol extension methods to be used directly on the protocol.
This would enable the Factory-pattern to be elegantly implemented as in the example below:

protocol P {}

class A : P {}
class B : P {}

extension P {
  static func factory(type: Bool) -> P { return type ? A() : B()}
}

let a = P.factory(false) // <- Not possible in Swift 2, factory() can only be used on either A or B

Regards

Rasmus Friis Kjeldsen

+1

I'd love to see this in the language.

Hope somebody from the core team will comment on why this is not allowed.

R+

···

Sent from my iPhone

On 27 Feb 2016, at 11:31, Rasmus Kjeldsen via swift-evolution <swift-evolution@swift.org> wrote:

I propose to allow static protocol extension methods to be used directly on the protocol.
This would enable the Factory-pattern to be elegantly implemented as in the example below:

protocol P {}

class A : P {}
class B : P {}

extension P {
  static func factory(type: Bool) -> P { return type ? A() : B()}
}

let a = P.factory(false) // <- Not possible in Swift 2, factory() can only be used on either A or B

Regards

Rasmus Friis Kjeldsen

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

Rasmus Kjeldsen via swift-evolution

I propose to allow static protocol extension methods to be used directly on the protocol.
This would enable the Factory-pattern to be elegantly implemented as in the example below:

protocol P {}

class A : P {}
class B : P {}

extension P {
  static func factory(type: Bool) -> P { return type ? A() : B()}
}

let a = P.factory(false) // <- Not possible in Swift 2, factory() can
only be used on either A or B

Regards

Rasmus Friis Kjeldsen

'extension P' extends all types that conform to P. We could introduce a new
syntax for extending the protocol type, maybe:

extension protocol<P> { ... }

This might be an important part of fleshing out our support for protocol
existential types. It would offer a way for protocol types to conform to
their own protocols even when Self or associated types make that normally
impossible.

-Joe

···

<swift-evolution@swift.org> wrote:

Maybe the “extension protocol <P>” syntax is more confusing than to just let protocol types implicitly conform to the static methods of their own protocol?
Below is an example of what I mean:

protocol P {}

extension P {
  static func a() {}
}

extension protocol <P> {
  static func b() {}
}

class C : P { }

C.a() // ok
C.b() // ok or not allowed?
P.a() // not allowed
P.b() // ok

And if C.b() is not allowed, what about if we also do:

extension P {
  static func b() {}
}
C.b() // ok?

Should that be allowed?

Regards
Rasmus

···

On 28 Feb 2016, at 02:05, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

Rasmus Kjeldsen via swift-evolution
<swift-evolution@swift.org> wrote:

I propose to allow static protocol extension methods to be used directly on the protocol.
This would enable the Factory-pattern to be elegantly implemented as in the example below:

protocol P {}

class A : P {}
class B : P {}

extension P {
  static func factory(type: Bool) -> P { return type ? A() : B()}
}

let a = P.factory(false) // <- Not possible in Swift 2, factory() can
only be used on either A or B

Regards

Rasmus Friis Kjeldsen

'extension P' extends all types that conform to P. We could introduce a new
syntax for extending the protocol type, maybe:

extension protocol<P> { ... }

This might be an important part of fleshing out our support for protocol
existential types. It would offer a way for protocol types to conform to
their own protocols even when Self or associated types make that normally
impossible.

-Joe

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

1 Like

Wouldn't it be easier (syntax wise) to extend the meta type directly? :

protocol P {}
extension P.Type {
        static func a() {}
}

Kind regards
- Maximilian

···

Am 28.02.2016 um 02:05 schrieb Joe Groff via swift-evolution <swift-evolution@swift.org>:

Rasmus Kjeldsen via swift-evolution
<swift-evolution@swift.org> wrote:

I propose to allow static protocol extension methods to be used directly on the protocol.
This would enable the Factory-pattern to be elegantly implemented as in the example below:

protocol P {}

class A : P {}
class B : P {}

extension P {
   static func factory(type: Bool) -> P { return type ? A() : B()}
}

let a = P.factory(false) // <- Not possible in Swift 2, factory() can
only be used on either A or B

Regards

Rasmus Friis Kjeldsen

'extension P' extends all types that conform to P. We could introduce a new
syntax for extending the protocol type, maybe:

extension protocol<P> { ... }

This might be an important part of fleshing out our support for protocol
existential types. It would offer a way for protocol types to conform to
their own protocols even when Self or associated types make that normally
impossible.

-Joe

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

Maybe the “extension protocol <P>” syntax is more confusing than to just let protocol types implicitly conform to the static methods of their own protocol?

Not every protocol type is a natural model of the protocol it represents. When a protocol has `static` or `init` requirements, for instance, the protocol type can't conform to its own protocol, since it's an abstract type with no type-level implementations of its own. Contravariant `Self` and associated type constraints are also problematic. Furthermore, in our resilience model, public protocols are allowed to add new requirements in future versions that cause a formerly self-conforming protocol to no longer conform. For these reasons, we really need the ability to explicitly declare that a protocol type is intended to be usable as a model of its own protocol.

protocol P {}

extension P {
  static func a() {}
}

extension protocol <P> {
  static func b() {}
}

class C : P { }

C.a() // ok

Yeah.

C.b() // ok or not allowed?

Good question. The type object `C.self: C.Type` is a subtype of `P.Type`, so one could argue that this should be allowed by implicit up-conversion.

P.a() // not allowed

Yeah.

P.b() // ok

Yeah.

And if C.b() is not allowed, what about if we also do:

extension P {
  static func b() {}
}
C.b() // ok?

Should that be allowed?

Yeah, in that case `extension P` is extending all types that conform to P.

-Joe

···

On Feb 29, 2016, at 11:28 PM, Rasmus Kjeldsen <rasmus.kjeldsen@gmail.com> wrote:

Regards
Rasmus

On 28 Feb 2016, at 02:05, Joe Groff via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Rasmus Kjeldsen via swift-evolution
<swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I propose to allow static protocol extension methods to be used directly on the protocol.
This would enable the Factory-pattern to be elegantly implemented as in the example below:

protocol P {}

class A : P {}
class B : P {}

extension P {
  static func factory(type: Bool) -> P { return type ? A() : B()}
}

let a = P.factory(false) // <- Not possible in Swift 2, factory() can
only be used on either A or B

Regards

Rasmus Friis Kjeldsen

'extension P' extends all types that conform to P. We could introduce a new
syntax for extending the protocol type, maybe:

extension protocol<P> { ... }

This might be an important part of fleshing out our support for protocol
existential types. It would offer a way for protocol types to conform to
their own protocols even when Self or associated types make that normally
impossible.

-Joe

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

Maybe we'd allow that too, but you wouldn't want 'static' there, since that would put methods on P.Type.Type rather than P.Type.

-Joe

···

On Mar 23, 2016, at 7:13 AM, Maximilian Hünenberger <m.huenenberger@me.com> wrote:

Wouldn't it be easier (syntax wise) to extend the meta type directly? :

protocol P {}
extension P.Type {
      static func a() {}
}

I'm not sure if this is an appropriate conversation to bring this up on but, here goes.

I have a need, for serialisation and various other uses, to be able to create an instance of a generic type, with the parameter type(s) stored in (a) var(s) of Any.Type.

Typically, the origin of the metatype is something like a Mirror's subjectType property.

Some of the proposals for Swift 3 seem to touch on similar needs but I was not sure whether to pollute those discussions or not

Background…

I am an experienced frameworks developer, with over 20 years experience in the field but much of the first half of that was in C#, where such things are handled very well by the reflection provided in .NET.

I now also have 10 years Objective-C experience and started playing with Swift from the get-go but am finding the limitations of Swift's reflection to be very frustrating, especially for such a common use case as serialisation.

Joanna

···

--
Joanna Carter
Carter Consulting

You can accomplish this with Swift today by casting your Any.Type to a Protocol.Type that provides an initializer:

protocol Deserializable {
  init(deserializedFrom stream: DeserializationStream)
}

func deserializeValue(type type: Any.Type, from stream: DeserializationStream) -> Any? {
  if let deserializableType = type as? Deserializable.Type {
    return deserializableType.init(deserializedFrom: stream)
  }
  return nil
}

-Joe

···

On Mar 23, 2016, at 10:13 AM, Joanna Carter via swift-evolution <swift-evolution@swift.org> wrote:

I'm not sure if this is an appropriate conversation to bring this up on but, here goes.

I have a need, for serialisation and various other uses, to be able to create an instance of a generic type, with the parameter type(s) stored in (a) var(s) of Any.Type.

Typically, the origin of the metatype is something like a Mirror's subjectType property.

Some of the proposals for Swift 3 seem to touch on similar needs but I was not sure whether to pollute those discussions or not

My apologies. I seem to have managed to get this subject as a reply to another. I'm not accustomed to mailing list forums. Could an admin move it if necessary?

Joanna

···

--
Joanna Carter
Carter Consulting

Hi Joe

You can accomplish this with Swift today by casting your Any.Type to a Protocol.Type that provides an initializer:

protocol Deserializable {
init(deserializedFrom stream: DeserializationStream)
}

func deserializeValue(type type: Any.Type, from stream: DeserializationStream) -> Any? {
if let deserializableType = type as? Deserializable.Type {
   return deserializableType.init(deserializedFrom: stream)
}
return nil
}

Hmm… I've been playing with this for days now and, as useful as your code is for instantiating a given type, what I need to do is instantiate a generic type, bound to that given type.

Something along the lines of…

public protocol PropertyProtocol
{
  var untypedValue: Any? { get }
}

public struct Property<PropertyType : Any>
{
  public let info: PropertyInfo
  
  public var name: String
  {
    return info.name
  }
  
  public var displayName: String
  {
    return info.displayName
  }
  
  public var value: PropertyType?
  
  public init()
  {
    self.init(propertyInfo: PropertyInfo(), value: nil)
  }
  
  init(propertyInfo: PropertyInfo, value: PropertyType?)
  {
    self.value = value
    
    self.info = propertyInfo;
  }
  
  init(propertyInfo: PropertyInfo)
  {
    self.init(propertyInfo: propertyInfo, value: nil)
  }
  
  init(other: Property<PropertyType>)
  {
    self.init(propertyInfo: other.info, value: other.value)
  }
}

struct PropertyFactory
{
  static func createBoundPropertywithValueType(valueType: Any.Type) -> PropertyProtocol
  {
    return Property<valueType>.init()
  }
}

Of course, Ive still got a lot of C# cruft in my thinking so I am more than willing to admit I may not be approaching this in the right way ;-)

Joanna

···

--
Joanna Carter
Carter Consulting

Hi Joe

You can accomplish this with Swift today by casting your Any.Type to a Protocol.Type that provides an initializer:

protocol Deserializable {
init(deserializedFrom stream: DeserializationStream)
}

func deserializeValue(type type: Any.Type, from stream: DeserializationStream) -> Any? {
if let deserializableType = type as? Deserializable.Type {
  return deserializableType.init(deserializedFrom: stream)
}
return nil
}

Hmm… I've been playing with this for days now and, as useful as your code is for instantiating a given type, what I need to do is instantiate a generic type, bound to that given type.

Something along the lines of…

public protocol PropertyProtocol
{
var untypedValue: Any? { get }
}

public struct Property<PropertyType : Any>
{
public let info: PropertyInfo

public var name: String
{
   return info.name
}

public var displayName: String
{
   return info.displayName
}

public var value: PropertyType?

public init()
{
   self.init(propertyInfo: PropertyInfo(), value: nil)
}

init(propertyInfo: PropertyInfo, value: PropertyType?)
{
   self.value = value

   self.info = propertyInfo;
}

init(propertyInfo: PropertyInfo)
{
   self.init(propertyInfo: propertyInfo, value: nil)
}

init(other: Property<PropertyType>)
{
   self.init(propertyInfo: other.info, value: other.value)
}
}

struct PropertyFactory
{
static func createBoundPropertywithValueType(valueType: Any.Type) -> PropertyProtocol
{
   return Property<valueType>.init()
}
}

Of course, Ive still got a lot of C# cruft in my thinking so I am more than willing to admit I may not be approaching this in the right way ;-)

Sorry for not getting back to you sooner. What's necessary here is a way to "open" the type of Any.Type, turning it back into something the compiler considers as a type instead of a value. This is something we've discussed having better support for, but right now there's only a sneaky way to do it using protocol extensions. If you constrain Property's type parameter to a protocol:

protocol PropertyType {}
struct Property<T: PropertyType>: PropertyProtocol {}

then you can add a static method to PropertyType in an extension, which can use Self as the opened dynamic type:

extension PropertyType {
  private static func create() -> Property<Self> {
    return Property<Self>.init()
  }
}

and delegate to that extension method in your factory function:

struct PropertyFactory {
  static func createBoundPropertywithValueType(valueType: PropertyType.Type) -> PropertyProtocol
  {
    return valueType.create()
  }
}

-Joe

···

On Mar 23, 2016, at 11:29 AM, Joanna Carter <joanna@carterconsulting.org.uk> wrote:

struct PropertyFactory
{
static func createBoundPropertywithValueType(valueType: Any.Type) -> PropertyProtocol
{
   return Property<valueType>.init()
}
}

Well, I've been playing some more and come up with an idea that seems to be thwarted by the compiler…

protocol PropertyFactory
{
  
}

extension PropertyFactory
{
  // what I'd like to do but that doesn't compile
  static func createProperty() -> PropertyProtocol
  {
    return Property<Self.Type>()
  }
  
// what it seems I have to do to satisfy the compiler
  static func createProperty(valueType: Any.Type) -> PropertyProtocol
  {
    switch self // this is a really naff and limited workaround
    {
      case is Int.Type:
        return Property<Int>()
      // ...
      default: fatalError("Unexpected type \(self)")
    }
  }
}

extension Int : PropertyFactory
{
  
}

// calling code works fine

    let type: PropertyFactory.Type = Int.self
    
    var property = type.createProperty(type) as? Property<Int>
      
    property?.value = 123
      
    print(property?.untypedValue)
    
… but it does mean that all possible types would have to be extended to implement PropertyFactory.

Does this make a case for either :

Extend the generic syntax to allow for something like

struct GenericStruct<ValueType : Any.Type>
{
  func doSomething<ParamType : MyType.Type>()
  {
    …
  }
}

… or having something like the following that knows how to bind a generic type to its parameter types …

protocol AnyGeneric // all generic types implement this protocol
{

}

extension AnyGeneric
{
  static func bindToParameterType(parameterType: Any.Type) -> Any.Type
  {
    // return something akin to Self<parameterType>
  }
}

The more I think about it, the latter option seems slightly screwy but I include it as a discussion point.

Joanna

···

--
Joanna Carter
Carter Consulting

I want to thank you for your "rubber duck" consultancy; I now have a working factory mechanism…

protocol PropertyFactory { }

extension PropertyFactory
{
  static func createProperty() -> PropertyProtocol
  {
    typealias ValueType = Self
    
    return Property<ValueType>()
  }
}

extension Int : PropertyFactory { }

The key was to typealias Self so that it could then be passed as a type parameter to the generic type.

The problem remains that it is still necessary to implement PropertyFactory on each and every type that is likely to be used for a property.

I would therefore ask if anything that would make life simpler in this regard is a worthwhile candidate for inclusion in the future of Swift?

Joanna

···

--
Joanna Carter
Carter Consulting

Hi Joe

Sorry for not getting back to you sooner.

Hey, I'm guessing you're busier than I am ;-)

What's necessary here is a way to "open" the type of Any.Type, turning it back into something the compiler considers as a type instead of a value.

Yes, that's what I had in mind

This is something we've discussed having better support for, but right now there's only a sneaky way to do it using protocol extensions. If you constrain Property's type parameter to a protocol:

protocol PropertyType {}
struct Property<T: PropertyType>: PropertyProtocol {}

then you can add a static method to PropertyType in an extension, which can use Self as the opened dynamic type:

extension PropertyType {
  private static func create() -> Property<Self> {
    return Property<Self>.init()
  }
}

Unfortunately, this doesn't compile…

extension PropertyType
{
  private static func create() -> Property<Self>
  {
    return Property<Self>.init()
                                        ^ '>' is not a postfix unary operator
  }
}

Which is why I found it necessary to add a typealias like so…

extension PropertyType
{
  private static func create() -> Property<Self>
  {
    typealias SelfType = Self
    
    return Property<SelfType>.init()
  }
}

and delegate to that extension method in your factory function:

struct PropertyFactory {
  static func createBoundPropertywithValueType(valueType: PropertyType.Type) -> PropertyProtocol
  {
    return valueType.create()
  }
}

This doesn't compile either…

struct PropertyFactory
{
  static func createBoundPropertywithValueType(valueType: PropertyType.Type) -> PropertyProtocol
  {
    return valueType.create()
                ^ Member 'create' cannot be used on value of protocol type 'PropertyType'; use a generic constraint instead
  }
}

For some reason, as soon as you change the PropertyType extension static func to return PropertyProtocol…

extension PropertyType
{
  private static func create() -> PropertyProtocol
  {
    typealias SelfType = Self
    
    return Property<SelfType>.init()
  }
}

… then, and only then, the factory's method compiles correctly.

Am I really finding bugs or am I still doing something wrong?

Joanna

···

--
Joanna Carter
Carter Consulting

Unfortunately, this doesn't compile…

extension PropertyType
{
private static func create() -> Property<Self>
{
   return Property<Self>.init()
                                       ^ '>' is not a postfix unary operator
}
}

Which is why I found it necessary to add a typealias like so…

extension PropertyType
{
private static func create() -> Property<Self>
{
   typealias SelfType = Self

   return Property<SelfType>.init()
}
}

Oops, that's definitely a bug.

This doesn't compile either…

struct PropertyFactory
{
static func createBoundPropertywithValueType(valueType: PropertyType.Type) -> PropertyProtocol
{
   return valueType.create()
               ^ Member 'create' cannot be used on value of protocol type 'PropertyType'; use a generic constraint instead
}
}

For some reason, as soon as you change the PropertyType extension static func to return PropertyProtocol…

extension PropertyType
{
private static func create() -> PropertyProtocol
{
   typealias SelfType = Self

   return Property<SelfType>.init()
}
}

… then, and only then, the factory's method compiles correctly.

Oops, my fault, this one is by design. Since we're calling through a dynamic type, our type system isn't able to express the result type `Property<valueType>`, so we need to abstract it behind the PropertyProtocol.

Of course, after all this effort, I still have the problem of how to get the Any.Type from the SubjectType property of a Mirror into a PropertyType.Type.

That one's easy, at least—you can take `Any.Type` and use `as?` to cast it to `PropertyType.Type`.

What would really make life easier here would be the ability to extend Any.

Any chance?

I think that'd be cool. Some people fear the power (and ensuing responsibility) that unleashes, though…

-Joe

···

On Mar 24, 2016, at 4:04 PM, Joanna Carter <joanna@carterconsulting.org.uk <mailto:joanna@carterconsulting.org.uk>> wrote:

Of course, after all this effort, I still have the problem of how to get the Any.Type from the SubjectType property of a Mirror into a PropertyType.Type.

What would really make life easier here would be the ability to extend Any.

Any chance?

Joanna

···

--
Joanna Carter
Carter Consulting

extension PropertyType
{
private static func create() -> Property<Self>
{
   typealias SelfType = Self

   return Property<SelfType>.init()
}
}

Oops, that's definitely a bug.

Excellent! I would also say that I have had a different error with…

  private static func create() -> PropertyProtocol
  {
    return Property<Self>() // without the explicit .init
                   ^ Non-associative operator is adjacent to operator of the same precedence
                   ^ No'>' candidates produce the expected contextual result type 'PropertyProtocol'
  }

So, it would seem that omitting the .init and using Self is interpreted differently to using the .init and using Self. I'd hate you to solve one bug just to find another for a very similar use case :-)

Oops, my fault, this one is by design. Since we're calling through a dynamic type, our type system isn't able to express the result type `Property<valueType>`, so we need to abstract it behind the PropertyProtocol.

Understood and somewhat expected. It's just that the error message is somewhat distant from the error site and seemingly not connected.

Of course, after all this effort, I still have the problem of how to get the Any.Type from the SubjectType property of a Mirror into a PropertyType.Type.

That one's easy, at least—you can take `Any.Type` and use `as?` to cast it to `PropertyType.Type`.

You'd have thought so wouldn't you. But although this works fine…

      let childMirror = Mirror(reflecting: child.value)
      
      print(child.label, child.value, childMirror.subjectType)
      
      let subjectType = childMirror.subjectType
      
      print(subjectType)
      
      if let propertyType = subjectType as? PropertyType.Type
      {
        let property = PropertyFactory.createProperty(propertyType)
        
        print(property)
      }

… this doesn't…

    let intType: Any.Type = Int.self
    
    print(intType)
    
    if let propertyType = intType as? PropertyType.Type // this cast fails
    {
      if var property = PropertyFactory.createProperty(propertyType) as? Property<Int?>
      {
        property.value = 123
        
        print(property)
      }
    }

What would really make life easier here would be the ability to extend Any.

Any chance?

I think that'd be cool. Some people fear the power (and ensuing responsibility) that unleashes, though…

Of course there will always be abusers of power but, by not permitting it, low level framework code becomes more difficult, onerous and obfuscated to write. Framework writers have always had to bear far more responsibility - think Cocoa ;-)

Joanna

···

--
Joanna Carter
Carter Consulting

extension PropertyType
{
private static func create() -> Property<Self>
{
typealias SelfType = Self

return Property<SelfType>.init()
}
}

Oops, that's definitely a bug.

Excellent! I would also say that I have had a different error with…

private static func create() -> PropertyProtocol
{
  return Property<Self>() // without the explicit .init
                 ^ Non-associative operator is adjacent to operator of the same precedence
                 ^ No'>' candidates produce the expected contextual result type 'PropertyProtocol'
}

So, it would seem that omitting the .init and using Self is interpreted differently to using the .init and using Self. I'd hate you to solve one bug just to find another for a very similar use case :-)

Oops, my fault, this one is by design. Since we're calling through a dynamic type, our type system isn't able to express the result type `Property<valueType>`, so we need to abstract it behind the PropertyProtocol.

Understood and somewhat expected. It's just that the error message is somewhat distant from the error site and seemingly not connected.

Of course, after all this effort, I still have the problem of how to get the Any.Type from the SubjectType property of a Mirror into a PropertyType.Type.

That one's easy, at least—you can take `Any.Type` and use `as?` to cast it to `PropertyType.Type`.

You'd have thought so wouldn't you. But although this works fine…

    let childMirror = Mirror(reflecting: child.value)

    print(child.label, child.value, childMirror.subjectType)

    let subjectType = childMirror.subjectType

    print(subjectType)

    if let propertyType = subjectType as? PropertyType.Type
    {
      let property = PropertyFactory.createProperty(propertyType)

      print(property)
    }

… this doesn't…

  let intType: Any.Type = Int.self

  print(intType)

  if let propertyType = intType as? PropertyType.Type // this cast fails
  {
    if var property = PropertyFactory.createProperty(propertyType) as? Property<Int?>
    {
      property.value = 123

      print(property)
    }
  }

You'd need to extend Int to conform to the protocol for the cast to succeed.

-Joe

···

On Mar 25, 2016, at 3:21 AM, Joanna Carter <joanna@carterconsulting.org.uk> wrote:

What would really make life easier here would be the ability to extend Any.

Any chance?

I think that'd be cool. Some people fear the power (and ensuing responsibility) that unleashes, though…

Of course there will always be abusers of power but, by not permitting it, low level framework code becomes more difficult, onerous and obfuscated to write. Framework writers have always had to bear far more responsibility - think Cocoa ;-)

Joanna

--
Joanna Carter
Carter Consulting

You'd need to extend Int to conform to the protocol for the cast to succeed.

Ah, yes. And there lies the reason behind wanting to be able to extend Any - avoiding having to extend every possible type likely to be used - mutter, mutter ;-)

Is it worth putting i a formal proposal for the extension of Any, or has that already been done?

Joanna

···

--
Joanna Carter
Carter Consulting