[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