<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/xhtml; charset=utf-8">
</head>
<body>
<div style="font-family:sans-serif"><div style="white-space:normal">
<p dir="auto">Hi Joanna,</p>
<p dir="auto">Your example doesn’t work for a few reasons:</p>
<ol>
<li value="1"><p dir="auto">You’re getting a compiler error because of the difference between the representation of metatypes (<code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Foo.Type</code> for some type <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Foo</code>) of concrete types (e.g. <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">String</code>, <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Int</code>), and protocols (e.g. <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">DefaultValueProvider</code>). Some of the compiler folks can correct my exact use of terminology here, but the essence is this: when you <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">as?</code>-cast a type to a concrete type (e.g. <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">type as? String.self</code>), you’ll get a concrete metatype which can be used dynamically as you would a concrete type elsewhere. When you <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">as?</code>-cast a type to a protocol type (<code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">type as? DefaultValueProvider</code>), 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. <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">(type as! DefaultValueProvider).init()</code>), but the concrete type is not known. You can see this with a small example:</p>
<pre style="background-color:#F7F7F7; border-radius:5px 5px 5px 5px; margin-left:15px; margin-right:15px; max-width:90vw; overflow-x:auto; padding:5px; color:black" bgcolor="#F7F7F7"><code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0" bgcolor="#F7F7F7"><span style="color: #008800; font-weight: bold">protocol</span> <span style="color: #BB0066; font-weight: bold">P</span> {
<span style="color: #008800; font-weight: bold">init</span>()
}
<span style="color: #008800; font-weight: bold">extension</span> <span style="color: #BB0066; font-weight: bold">Int</span> : P {}
<span style="color: #008800; font-weight: bold">let</span> <span style="color: #996633">type</span>: <span style="color: #007020">Any</span>.<span style="color: #008800; font-weight: bold">Type</span> = <span style="color: #007020">Int</span>.<span style="color: #008800; font-weight: bold">self</span>
<span style="color: #008800; font-weight: bold">if</span> <span style="color: #008800; font-weight: bold">let</span> <span style="color: #996633">specific</span> = type <span style="color: #008800; font-weight: bold">as</span>? P.<span style="color: #008800; font-weight: bold">Type</span> {
<span style="color: #888888">// type of specific.init() is P, not Int</span>
<span style="color: #007020">print</span>(specific.<span style="color: #008800; font-weight: bold">init</span>())
}
</code></pre>
<p dir="auto">This, then coincides with:</p></li>
<li value="2"><p dir="auto">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:</p>
<pre style="background-color:#F7F7F7; border-radius:5px 5px 5px 5px; margin-left:15px; margin-right:15px; max-width:90vw; overflow-x:auto; padding:5px; color:black" bgcolor="#F7F7F7"><code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0" bgcolor="#F7F7F7"><span style="color: #008800; font-weight: bold">protocol</span> <span style="color: #BB0066; font-weight: bold">P</span> {}
<span style="color: #008800; font-weight: bold">extension</span> <span style="color: #BB0066; font-weight: bold">Int</span> : P {}
<span style="color: #008800; font-weight: bold">func</span> <span style="color: #0066BB; font-weight: bold">foo</span><T>(<span style="color: #008800; font-weight: bold">_</span> type: T.<span style="color: #008800; font-weight: bold">Type</span>) {
<span style="color: #007020">print</span>(type)
}
<span style="color: #008800; font-weight: bold">let</span> <span style="color: #996633">type</span>: <span style="color: #007020">Any</span>.<span style="color: #008800; font-weight: bold">Type</span> = <span style="color: #007020">Int</span>.<span style="color: #008800; font-weight: bold">self</span>
foo(type) <span style="color: #888888">// cannot invoke 'foo' with an argument list of type '(Any.Type)'</span>
foo(type <span style="color: #008800; font-weight: bold">as</span>! P.<span style="color: #008800; font-weight: bold">Type</span>) <span style="color: #888888">// cannot invoke 'foo' with an argument list of type '(P.Type)'</span>
foo(type <span style="color: #008800; font-weight: bold">as</span>! <span style="color: #007020">Int</span>.<span style="color: #008800; font-weight: bold">Type</span>) <span style="color: #888888">// works just fine</span>
</code></pre>
<p dir="auto">Arguments to generic methods <em>must</em> be statically knowable for the method to be dispatched; otherwise you’ll get the compiler error that you see.</p></li>
</ol>
<p dir="auto">This is also why you can’t <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">decode(Decodable.self)</code> or <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">decode(Any.self)</code> — those are not concrete types.<br>
(On a side note, though, this would never be possible with the <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Decodable</code> protocol. We need a concrete type to decode because values are otherwise ambiguous. Is <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">5</code> an <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Int8</code>, <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">UInt8</code>, …, <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Int</code>, <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">UInt</code>, …, <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Int64</code>, <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">UInt64</code>, <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Float</code>, or <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Double</code>? Is <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">"2017-07-14"</code> a <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">String</code> or a <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Date</code>? What would you expect to get for <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">decode(Decodable.self)</code>?)</p>
<p dir="auto">— Itai</p>
<p dir="auto">On 13 Jul 2017, at 11:46, Joanna Carter via swift-users wrote:</p>
</div>
<div style="white-space:normal"><blockquote style="border-left:2px solid #777; color:#777; margin:0 0 5px; padding-left:5px"><p dir="auto">Greetings<br>
<br>
I notice that, with Swift 4, I can decode an object like this :<br>
<br>
        • let retrievedSpot = try decoder.decode(ParkingSpot.self, from: retrievedData)<br>
<br>
And that will return a ParkingSpot, as expected, into retrievedSpot.<br>
<br>
However, I thought I would try the same technique with one of my pet projects…<br>
<br>
I have a protocol and an implementing struct :<br>
<br>
        • public protocol PropertyProtocol<br>
        • {<br>
        • static var propertyType: Any.Type { get }<br>
        •<br>
        • var untypedValue: Any? { get }<br>
        • }<br>
        •<br>
        • public struct Property<valueT : DefaultValueProvider> : PropertyProtocol<br>
        • {<br>
        • public static var propertyType: Any.Type<br>
        • {<br>
        • return valueT.self<br>
        • }<br>
        •<br>
        • public var untypedValue: Any?<br>
        • {<br>
        • return value<br>
        • }<br>
        •<br>
        • public var value = valueT()<br>
        • }<br>
<br>
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 :<br>
<br>
        • struct PropertyFactory<br>
        • {<br>
        • static func createProperty<T>(_ type: T.Type) -> PropertyProtocol where T : DefaultValueProvider<br>
        • {<br>
        • return type.createProperty()<br>
        • }<br>
        • }<br>
<br>
DefaultValueProvider is defined as follows and String is extended to conform to it :<br>
<br>
        • public protocol DefaultValueProvider<br>
        • {<br>
        • init()<br>
        • }<br>
        •<br>
        • extension String : DefaultValueProvider { }<br>
<br>
Now, this works fine if I pass a known type :<br>
<br>
        • let pproperty = PropertyFactory.createProperty(String.self)<br>
<br>
But, if I hold the type to be passed in in an Any.Type or DefaultValueProvider.Type variable, then doing this :<br>
<br>
        • let type: Any.Type = String.self<br>
        •<br>
        • if let propertyType = type as? DefaultValueProvider.Type<br>
        • {<br>
        • let p = PropertyFactory.createProperty(propertyType)<br>
        •<br>
        • …<br>
<br>
Fails to compile with the message : Cannot invoke 'createProperty' with an argument list of type '(DefaultValueProvider.Type)'<br>
<br>
Likewise Decoder.decode(…) will not accept storing the type in an Any.Type or Decodable.Type variable.<br>
<br>
I find this odd and perplexing. Is this a known issue or has nobody yet realised that this could be useful ?<br>
<br>
Joanna<br>
<br>
--<br>
Joanna Carter<br>
Carter Consulting<br>
_______________________________________________<br>
swift-users mailing list<br>
swift-users@swift.org<br>
<a href="https://lists.swift.org/mailman/listinfo/swift-users" style="color:#777">https://lists.swift.org/mailman/listinfo/swift-users</a></p>
</blockquote></div>
<div style="white-space:normal">
</div>
</div>
</body>
</html>