[swift-users] Swift 4 emulating Decoder behaviour

Itai Ferber iferber at apple.com
Fri Jul 14 17:13:51 CDT 2017


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 at swift.org
> https://lists.swift.org/mailman/listinfo/swift-users
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20170714/7696266d/attachment.html>


More information about the swift-users mailing list