As Operator Overload

0 as Int
0 as Float
0 as Double

Here, as is not use to check a type, but to define the literal expression type.

let i: Int = 0
let f = i as Float
let d = i as Double

Here as is used to convert an integer value from one type to another.

So while as and as? can also be used for type checking, this is not the only usage it has in the language.
What I mean is that if you fear the as semantic be messed up by the proposed change, you can be reassured, it has already be done a long time ago.

A lot of engineering went into making sure that Swift String will continue to bridge seamlessly with UTF-16 NSString instances. The String storage class is an actual subclass of NSString at runtime.

1 Like

Here, you are indeed attempting to use as to convert between types, and it is invalid in Swift because as is not a conversion operator but a coercion operator. The error is:

Cannot convert value of type ‘Int’ to type ‘Float’ in coercion

Yes, but String's storage is not String. You actually convert from a NSString to a String with a custom storage, so there is actually a conversion here IMHO.

My bad. I'm so used to have as everywhere in my code to bridge types that I forgot it didn't work for numeric types.

I’m not sure what you mean by this. When you bridge an NSString to String, its representation in memory is unchanged, and all operations are handled through resilient function calls. When bridging in the other direction, String storage is an NSString subclass, as I’ve already cited previously. That there are optimizations possible to improve string performance in no way changes the semantics or syntax of the language, where as is a coercion operator that does not convert between types.

I mean that this is not real toll-free as in Obj-C where the casting required zero instruction as it was just a casting.

In Swift, you still have to execute code to create a String value, populate it with the NSString and set the right flags to tell Swift to handle it specially.

This proposition is about letting custom type support that kind of bridging / conversion, where the compiler generate a function call to convert a type into an other type.

You may argue that in case of NSString<->String, this is more a wrapping that a conversion, but the principle is the same. You still need to call a special function that convert a representation into an other one (either by wrapping it or converting it).

And String is not the only bridges types. When you use as on an NSNumber to convert it to a base type, there is a real type conversion, not just a wrapping of the underlying value.

1 Like

I'm not native English, so maybe my understanding of coercion is not right, but from Wikipedia, coercion is just an implicit conversion.

No, you can’t use as for NSNumber bridging. You must use as? or as!: these are distinct operators with semantics even more complicated than those of as. Critically, those are not coercion operators like as is.

Absolutely can.

import Foundation
print(true as NSNumber)

Compiles, runs, prints "1".

1 Like

That is not bridging. That is telling Swift that you want the literal true to be of type NSNumber. There is no other type involved. See the examples given above for literals.

1 Like
import Foundation
let b: Bool = true
print(b as NSNumber)

Same result.

1 Like

Ah, right. This is to imitate a subtyping relationship between Bool and NSNumber. Confusing, though, as you can see. Full of subtleties as well.

There’s been talk about whether this feature is a good idea at all, and as you can guess I would be in favor of eventually removing it.

Very confusing. Especially as you are right telling that you can't bridge from NSNumber to a numerical type, but only from a numerical/boolean type to NSNumber.

Sorry for the confusion. When I said bridging I didn't mean as in toll-free bridging. I meant bridging simply in as a way to bridge together two different APIs like the Swift Standard Library and Foundation as in the String and NSString example. Talking about this specific example; The toll-free bridging between String and NSString is just an implementation detail. An important one, sure, but a detail nevertheless. The important aspect here is the semantics of the as operator in someString as NSString, which it pretty straightforward, "treat this type as this type". It doesn't matter if it's toll-free bridging, coercion, conversion, etc. This shouldn't make a difference and certainly, I don't think we should add yet another keyword for conversion.

I disagree, I don't think using as to convert between Bool and NSNumber is confusing. From an API perspective, it makes a lot of sense. Again, that's super straightforward, as would HTTPRequest as URLRequest be. I don't think it really matters "how" the as operator is working beneath the cover. On the other hand, I think the language would benefit a lot in having an idiomatic and consistent way to express "treat this type as this type" for our custom types as well.

Swift has standardized on OneType(instanceOfAnotherType) as the idiomatic way of converting between two types. It is not the role of as.

Those are not the semantics of the as operator. Bridging between Foundation and standard library types has been engineered carefully to maximize interoperability between Swift and Objective-C APIs. That characteristic is not an “implementation detail” but rather a tentpole feature of Swift. In other words, it would not have been acceptable to change the implementation of Swift String so that bridging from NSString involved eagerly converting from UTF-16 to UTF-8.

Now, if you want to discuss the possibility of value subtyping relationships...

2 Likes

This.

Right, this is why I'm proposing the overload of the as operator. Initializing a type with another type with no label is not always used to convey conversion. One example using SwitUI:

Text("Hello world!")

We're not converting a String to Text here, we're initializing Text with a String (using the StringProtocol, but with a String nonetheless). Text here is a View used for UI, not a container for characters as String is.

Now let's compare:

URLRequest(httpRequest)
// vs
httpRequest as URLRequest

The version with the as operator is much more clearer.

I honestly don't understand why you're insisting on the topic of toll-free bridging. The as operator is used as a way to perform toll-free bridging, but it's not the only use of the as operator. Right now I think we've seen three different uses just in this conversation:

Toll-free bridging as in:

let string: String = "Hello world!" 
let nsstring = string as NSString

Conversion to imitate a subtyping relationship (If I understood correctly) as in:

let bool: Bool = true
let nsnumber = bool as NSNumber

Subtyping relationship as in:

class Parent {}
class Child : Parent {}
let child: Child = Child()
let parent = child as Parent

or

protocol Protocol {}
struct Child : Protocol {}
let child: Child = Child()
let protocol = child as Protocol

From the type system perspective, the underlying method which is used by the as operator is irrelevant. All I want to be able to do is to state that one type can be expressed as another. Now my question is; If we already have toll-free bridging and conversion for Foundation types using the as operator, why can't we have them for our custom types?

1 Like

As you’ve shown, “as” has a variety of meanings.

Do you mean a subtyping relationship? Value subtyping is certainly a large topic.

Do you mean you want custom toll-free bridging? That’s also an interesting topic, but is probably best tackled after Swift gets more facilities to control type layout.

But as is not equivalent to or an alternative to the standard syntax for converting between types, which is expressed as an initializer.