Hi Joanna,

Your example doesn’t work for a few reasons:

1. You’re getting a compiler error because of the difference between the representation of metatypes (`Foo.Type` for some type `Foo`) of concrete types (e.g. `String`, `Int`), and protocols (e.g. `DefaultValueProvider`). Some of the compiler folks can correct my exact use of terminology here, but the essence is this: when you `as?`-cast a type to a concrete type (e.g. `type as? String.self`), you’ll get a concrete metatype which can be used dynamically as you would a concrete type elsewhere. When you `as?`-cast a type to a protocol type (`type as? DefaultValueProvider`), however, you get a protocol-constrained metatype which is not concrete and cannot be used dynamically as you would statically. You can call statically-known protocol methods on the metatype (e.g. `(type as! DefaultValueProvider).init()`), but the concrete type is not known. You can see this with a small example:

  ```swift
  protocol P {
      init()
  }

  extension Int : P {}

  let type: Any.Type = Int.self
  if let specific = type as? P.Type {
      // type of specific.init() is P, not Int
      print(specific.init())
  }
  ```

  This, then coincides with:

2. The fact that methods generic on types can only take concrete type parameters. Protocol metatypes cannot be passed in as you would a concrete metatype:

  ```swift
  protocol P {}
  extension Int : P {}

  func foo<T>(_ type: T.Type) {
      print(type)
  }

  let type: Any.Type = Int.self
foo(type) // cannot invoke 'foo' with an argument list of type '(Any.Type)' foo(type as! P.Type) // cannot invoke 'foo' with an argument list of type '(P.Type)'
  foo(type as! Int.Type) // works just fine
  ```

Arguments to generic methods _must_ be statically knowable for the method to be dispatched; otherwise you’ll get the compiler error that you see.

This is also why you can’t `decode(Decodable.self)` or `decode(Any.self)` — those are not concrete types. (On a side note, though, this would never be possible with the `Decodable` protocol. We need a concrete type to decode because values are otherwise ambiguous. Is `5` an `Int8`, `UInt8`, …, `Int`, `UInt`, …, `Int64`, `UInt64`, `Float`, or `Double`? Is `"2017-07-14"` a `String` or a `Date`? What would you expect to get for `decode(Decodable.self)`?)

— Itai

On 13 Jul 2017, at 11:46, Joanna Carter via swift-users wrote:

Greetings

I notice that, with Swift 4, I can decode an object like this :

• let retrievedSpot = try decoder.decode(ParkingSpot.self, from: retrievedData)

And that will return a ParkingSpot, as expected, into retrievedSpot.

However, I thought I would try the same technique with one of my pet projects…

I have a protocol  and an implementing struct :

        • public protocol PropertyProtocol
        • {
        •   static var propertyType: Any.Type { get }
        •
        •   var untypedValue: Any? { get }
        • }
        •
• public struct Property<valueT : DefaultValueProvider> : PropertyProtocol
        • {
        •   public static var propertyType: Any.Type
        •   {
        •     return valueT.self
        •   }
        •
        •   public var untypedValue: Any?
        •   {
        •     return value
        •   }
        •
        •   public var value = valueT()
        • }

Now, I want to be able to use a "factory" method to create an instance of Property<T>, bound to its parameter type. So, I followed the same principal as Decoder :

        • struct PropertyFactory
        • {
• static func createProperty<T>(_ type: T.Type) -> PropertyProtocol where T : DefaultValueProvider
        •   {
        •     return type.createProperty()
        •   }
        • }

DefaultValueProvider is defined as follows and String is extended to conform to it :

        • public protocol DefaultValueProvider
        • {
        •   init()
        • }
        •
        • extension String : DefaultValueProvider { }

Now, this works fine if I pass a known type :

        • let pproperty = PropertyFactory.createProperty(String.self)

But, if I hold the type to be passed in in an Any.Type or DefaultValueProvider.Type variable, then doing this :

        •     let type: Any.Type = String.self
        •
        •     if let propertyType = type as? DefaultValueProvider.Type
        •     {
        •       let p = PropertyFactory.createProperty(propertyType)
        •
        •       …

Fails to compile with the message : Cannot invoke 'createProperty' with an argument list of type '(DefaultValueProvider.Type)'

Likewise Decoder.decode(…) will not accept storing the type in an Any.Type or Decodable.Type variable.

I find this odd and perplexing. Is this a known issue or has nobody yet realised that this could be useful ?

Joanna

--
Joanna Carter
Carter Consulting
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Reply via email to