<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">+1. I’ve always found the bridging casting behavior confusing. Definitely would agree with removing it—especially given the interop problems—but only if a good solution was found for imported APIs.<div class=""><br class=""></div><div class="">With `NSNumber`-involving APIs, I think it might be reasonable to add an Objective-C annotation to specify what numeric type ought to be used (instead of Any). For example, `-(NSNumber<UInt32> *) foo` is a straw-man syntax for specifying that a number should be imported as `UInt32` in Swift.</div><div class=""><br class=""></div><div class="">What other APIs involve this dynamic through-`Any` bridging behavior? It might make sense to make a comprehensive list. Another I can think of is `NSJSONSerialization`. It would be much more difficult to remove bridging behavior here without new language features… Additionally, NSNumber is still problematic—what should be the numeric type of a JSON number? If only we had integer and floating point existentials…</div><div class=""><br class=""></div><div class="">Perhaps Objective-C APIs that return `id`, when called from Swift, ought to <b class="">eagerly perform bridging</b> for the boxed value into the respective Swift type? Then `as` would no longer perform this weird behavior; it would just happen by default <b class="">for all values return from Objective-C APIs</b>. No need to store an extra bit on the existential and introduce inconsistent behavior… </div><div class=""><br class=""></div><div class="">There obviously ought to be a way to opt out of this if you want the original type. Perhaps the compiler could detect conversions back to Objective-C types are <b class="">optimize away the round-trip</b>? For example, `try! JSONSerialization.jsonObject(with: data)` would be implicitly converting to a Swift type (wrapped in an `Any`) while `NSString(try! JSONSerialization.jsonObject(with: data) as! String)` would skip the roundtrip conversion via the optimizer.</div><div class=""><br class=""></div><div class="">Sorry for the stream of consciousness, but hopefully I was able to contribute some useful ideas :)</div><div class=""><br class=""></div><div class=""><div class="">Cheers,</div><div class="">Jaden Geller<br class=""><div class=""><br class=""><div class=""><div><blockquote type="cite" class=""><div class="">On Mar 1, 2017, at 8:11 PM, Joe Groff via swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><meta http-equiv="Content-Type" content="text/html charset=utf-8" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; margin: 1.3125em 0px; font-size: 1.1429em; line-height: 1.3125em;" class="">I’d like to investigate separating Objective-C bridging from the behavior of the <code style="line-height: 1;" class="">as</code>/<code style="line-height: 1;" class="">as?</code>/<code style="line-height: 1;" class="">is</code> operator family again for Swift 4. Last year, I proposed <a href="https://github.com/apple/swift-evolution/blob/master/proposals/0083-remove-bridging-from-dynamic-casts.md" style="color: rgb(13, 110, 161); text-decoration: none; transition: color 0.2s ease-in-out; -webkit-transition: color 0.2s ease-in-out;" class="">SE–0083</a>, but we deferred the proposal for lack of time to evaluate its impact. As complicating factors, we now have source compatibility with Swift 3 as a requirement, and the <code style="line-height: 1;" class="">id</code>-as-<code style="line-height: 1;" class="">Any</code> work from <a href="https://github.com/apple/swift-evolution/blob/master/proposals/0116-id-as-any.md" style="color: rgb(13, 110, 161); text-decoration: none; transition: color 0.2s ease-in-out; -webkit-transition: color 0.2s ease-in-out;" class="">SE–0116</a> more or less requires bridging dynamic casts to work. I think we can nonetheless make important improvements in this area in order to simplify the core language and provide more portable behavior across platforms with and without ObjC interop. In retrospect, submitting SE–0083 as an omnibus “fix casting” proposal was a mistake. We can separate out a few smaller subproblems from the overall concept:</p><h2 id="replacingasforbridgingcoercion" style="color: rgb(17, 17, 17); font-size: 27px; line-height: 42px; margin-top: 42px; margin-bottom: 21px; font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif;" class="">Replacing <code style="line-height: 1;" class="">as</code> for bridging coercion</h2><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; margin: 1.3125em 0px; font-size: 1.1429em; line-height: 1.3125em;" class="">Swift 0 shipped with implicit conversions between standard library value types and their bridged Cocoa classes in both directions, and as we’ve eased off of the implicit conversions, we still left the <code style="line-height: 1;" class="">as</code> operator with the ability to force the conversions. This complicates the meaning of <code style="line-height: 1;" class="">as</code>: normally, it just provides type context, but it also has the power to force bridging conversions. These meanings are often at odds:</p><pre style="margin-top: 21px; margin-bottom: 21px; tab-size: 4; color: rgb(17, 17, 17); font-size: 15px; height: 204px; background-color: rgb(248, 248, 248);" class=""><code class="swift hljs" style="line-height: inherit; display: block; overflow-x: auto; padding: 0.5em; color: rgb(51, 51, 51); height: auto;"><span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// `NSNumber` is `ExpressibleByIntegerLiteral`, so this gives type context to the literal 0</span>
<span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// and is equivalent to `NSNumber(integerLiteral: 0)`</span>
<span class="hljs-number" style="color: rgb(0, 128, 128);">0</span> <span class="hljs-keyword" style="font-weight: bold;">as</span> <span class="hljs-type" style="color: rgb(68, 85, 136); font-weight: bold;">NSNumber</span>
<span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// `x` already has type `Int`, so this forces the bridging conversion and is equivalent to</span>
<span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// `_bridgeToObjectiveC(x)` (and thereby gives you a different kind of `NSNumber`!)</span>
<span class="hljs-keyword" style="font-weight: bold;">let</span> x: <span class="hljs-type" style="color: rgb(68, 85, 136); font-weight: bold;">Int</span> = <span class="hljs-number" style="color: rgb(0, 128, 128);">0</span>
x <span class="hljs-keyword" style="font-weight: bold;">as</span> <span class="hljs-type" style="color: rgb(68, 85, 136); font-weight: bold;">NSNumber</span></code></pre><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; margin: 1.3125em 0px; font-size: 1.1429em; line-height: 1.3125em;" class="">Aside from the complexity and non-portability of this behavior, this is also inconsistent with the Swift naming conventions, which recommend that conversions between related types be presented as initializers. Additionally, the bridging conversions often have specialized behavior for performance or semantic reasons that aren’t intended to be exposed in the normal API of either type (for example, bridging a Swift number type to <code style="line-height: 1;" class="">NSNumber</code> produces a “type-preserving” instance of NSNumber so that the bridge doesn’t lose type information, even though <code style="line-height: 1;" class="">NSNumber</code>’s own API presents a type-agnostic numeric object). Therefore, I propose that we remove the bridging behavior from <code style="line-height: 1;" class="">as</code>, and provide APIs for conversion where they don’t yet exist. <code style="line-height: 1;" class="">as</code> is purely a compile-time construct with no runtime interaction, so the Swift 3 compatibility and ABI issues are much simpler than they are when runtime casting behavior becomes involved.</p><h2 id="warningonisasascaststhatstaticallyinducebridging" style="color: rgb(17, 17, 17); font-size: 27px; line-height: 42px; margin-top: 42px; margin-bottom: 21px; font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif;" class="">Warning on <code style="line-height: 1;" class="">is</code>/<code style="line-height: 1;" class="">as?</code>/<code style="line-height: 1;" class="">as!</code> casts that statically induce bridging</h2><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; margin: 1.3125em 0px; font-size: 1.1429em; line-height: 1.3125em;" class="">Without changing the runtime behavior of casting, we could still discourage users from using dynamic casting to perform bridging conversions when it’s statically evident that a bridging conversion is the only way a cast succeeds. For example:</p><pre style="margin-top: 21px; margin-bottom: 21px; tab-size: 4; color: rgb(17, 17, 17); font-size: 15px; height: 120px; background-color: rgb(248, 248, 248);" class=""><code class="swift hljs" style="line-height: inherit; display: block; overflow-x: auto; padding: 0.5em; color: rgb(51, 51, 51); height: auto;"><span class="hljs-function"><span class="hljs-keyword" style="font-weight: bold;">func</span> <span class="hljs-title" style="color: rgb(153, 0, 0); font-weight: bold;">abuseBridgingCasts</span><span class="hljs-params">(on object: AnyObject)</span></span> {
<span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// warning: dynamic cast requires a bridging conversion; use `Int(bridgedFrom:)` instead</span>
<span class="hljs-keyword" style="font-weight: bold;">let</span> <span class="hljs-number" style="color: rgb(0, 128, 128);">_</span> = object <span class="hljs-keyword" style="font-weight: bold;">as</span>? <span class="hljs-type" style="color: rgb(68, 85, 136); font-weight: bold;">Int</span>
}</code></pre><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; margin: 1.3125em 0px; font-size: 1.1429em; line-height: 1.3125em;" class="">This wouldn’t be perfect, since we wouldn’t be able to warn about fully dynamic casts, but it could help encourage users to write portable code that doesn’t rely on the Objective-C bridge in common situations.</p><h2 id="limitingwhentheruntimeallowsbridgingindynamiccasts" style="color: rgb(17, 17, 17); font-size: 27px; line-height: 42px; margin-top: 42px; margin-bottom: 21px; font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif;" class="">Limiting when the runtime allows bridging in dynamic casts</h2><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; margin: 1.3125em 0px; font-size: 1.1429em; line-height: 1.3125em;" class="">Ideally, we would be able to change runtime dynamic casting itself to not involve bridging. However, as I mentioned above, there are at least two situations where bridging dynamic casts are necessary to meet design requirements:</p><ol style="margin-top: 21px; margin-bottom: 21px; padding-left: 1.5em; list-style-position: inside; color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; font-size: 15px;" class=""><li style="font-size: 16.5px;" class="">To maintain compatibility with Swift 3 code</li><li style="font-size: 16.5px;" class="">For dynamic-casting <code style="line-height: 1;" class="">Any</code>s that were bridged from an Objective-C <code style="line-height: 1;" class="">id</code>, since we don’t know statically whether a bridge to any particular Swift type is needed</li></ol><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; margin: 1.3125em 0px; font-size: 1.1429em; line-height: 1.3125em;" class="">However, neither of these is an insurmountable barrier. For Swift 3 compatibility, if nothing else, we could ship a parallel set of runtime entry points for dynamic casting with the Swift 3 behavior that would be used when compiling code in Swift 3 mode (and those entry points could possibly be banished to a separate compatibility dylib to avoid weighing down the ABI-stable Swift runtime forever). For <code style="line-height: 1;" class="">id</code>-as-<code style="line-height: 1;" class="">Any</code> bridging, existentials could potentially carry a bit to indicate whether casting out of that particular value should admit bridging casts. Doing that has complications of its own—having otherwise equivalent <code style="line-height: 1;" class="">Any</code> values have different behavior is undeniably weird—but, if we can keep the behavior isolated to code that directly interfaces with ObjC, I think it’s worth investigating in the interest of making the overall language more predictable.</p><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; margin: 1.3125em 0px; font-size: 1.1429em; line-height: 1.3125em;" class="">-Joe</p></div>_______________________________________________<br class="">swift-evolution mailing list<br class=""><a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a><br class="">https://lists.swift.org/mailman/listinfo/swift-evolution<br class=""></div></blockquote></div><br class=""></div></div></div></div></body></html>