As Operator Overload

Really it's a matter of preference.

IMO

let aString = string to NSString

reads as normal language. result = Noun Verb Object. It's easy to instantly understand what's happening. Wheras

let aString = NSString(string)

feels a lot more..."syntaxy" or mathy. it's just result = Object(Noun)

Should this be added to Foundation right now?...maybe (probably) not. It's certainly not a major pain-point and there are more important things to deal with. However users can't really add it themselves so it's the only way.

Maybe eventually we'll have somethign like Kotlin's infix operator and we can start messing with things like this :slight_smile:

Hi Paulo,

It is unlikely the core team will consider a proposal to allow customization of the behavior of as on different types.

The best way to think of the as keyword is as a way to provide "type context". That is, the following things should be seen as equivalent:

let x = foo as Bar
let y: Bar = foo

The benefit of as being it's a lot more versatile than assigning to a variable i.e. you can do this:

// f can take any type
func f<T>(_ t: T) { ... }
// fix T to be of type Bar, not type Foo
f(foo as Bar)

...without having to create a temporary. But it's best not to think of the as is an operator doing the conversion. Rather the conversion happens because the compiler is willing to convert certain types to other types implicitly, and the as is a way of spelling out the types in question. But typed variable declarations or function arguments are another way.

So, rather than propose something focusing on as, the proposal should examine the potential for user-implemented implicit conversions between types (which could be triggered by type context, including via as).

A very broad "convert anything implicitly to anything else" feature is also likely to receive a lot of push back, as it is (IMO at least) somewhat out of keeping with Swift's philosophy of strong typing. The implicit conversions we do have – to existentials, wrapping in optionals, or to/from ObjC bridged types – cause a lot of confusion for users, challenging the considerable benefits these conveniences bring. Adding to this cause of confusion would be hard to justify.

The general trend has been to go in the other direction. For example, SE-83: Remove bridging conversion behavior from dynamic casts was deferred, but is a direction the core team endorsed at the time in principle, even if it might now be impractical for source-stability reasons.

Pitches for narrower proposals than arbitrary conversion might get more traction. The idea of user-defined subtypes is one that's often brought up. But that is also a deceptively large design space (should variance of function argument/return types be allowed for example, or implicit conversion of collections of the subtype) so would be a large undertaking.

Ben

17 Likes

From the Swift API Design Guidelines:

In initializers that perform value preserving type conversions, omit the first argument label , e.g. Int64(someUInt32)

(Source: Swift.org - API Design Guidelines)

1 Like

Thank you, Ben!

I don't think this is always true. Specifically, you can use as to "cast" a concrete type to an existential:

let number = 1
let existential = number as Encodable
MemoryLayout.size(ofValue: existential) // 40

This isn't "toll-free" because number and existential have different sizes. And it also works for converting an array to an array of existentials:

let numbers = Array(1...10_000)
let encodables = numbers as [Encodable]

MemoryLayout.size(ofValue: numbers[0]) // 8
MemoryLayout.size(ofValue: encodables[0]) // 40

The compiler effectively has to turn this into a map operation, making it O(n).