[swift-evolution] [Proposal] Built-in "bridge" feature

Jerome ALVES j.alves at me.com
Wed Jan 20 17:49:04 CST 2016


I'm not sure, do you suggest to always use the "as" keyword, or only in case where we want to move from "A" to "C" passing by "B" ?

In fact,I feel right about always needing to use the "as" keyword. It's true that this kind of feature could be too magical and cause some issues. Having to explicitly cast to bridged type could be a good compromise between readability and safety.


> Le 21 janv. 2016 à 00:37, davesweeris at mac.com a écrit :
> 
> I could be wrong, but I believe that implicit type conversion, in general, causes problems (which is why ImplicitlyUnwrappedOptionals are handled with “compiler magic” as opposed to a general language feature). How would you feel about reusing the as keyword?
> let a = A()
> doSomethingWithC(a as C) // Compiler could check if there are explicit bridging functions, and fallback to as’s current meaning if not
> 
> Either way, though, I’d love a short-hand way to convert between types.
> 
> - Dave Sweeris
> 
>> On Jan 20, 2016, at 15:27, Jerome ALVES via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>> 
>> Hi everyone,
>> 
>> This is my first message on the mailing list and I hope I'll do everything right :)
>> I've been through the mailing-list archive and I didn't see anything close to this proposal so I hope I didn't miss anything.
>> Introduction
>> 
>> Sometimes, there are several ways to represent the same thing. For instance NSURL and NSURLComponents are both used to deal with URLs, but according to the API you want to use you must have to make manual conversions from one type to another. My proposal is to add a built-in bridge feature into the Swift language to remove a lot of boilerplate code and increase readability.
>> Motivation
>> 
>> 1. It's a really convenient pattern 
>> 
>> Swift has several great solutions for types. Structs, Enums, and Protocols can do as much as Classes. You can define properties, methods, subscripts, you can extend them, etc.. This allowed developers to use some types where they weren't expected. For instance we can use an enum AppColors to define all colors used by an app, and as an enum can have properties, we can add a var color: UIColor on it to generate the associate UIColor (or NSColor)
>> 
>> Its really convenient but wherever we want to use this color, we need to call the .color property :
>> myLabel.textColor = AppColors.PrimaryLabelTextColor.color
>> 
>> We can also have a protocol ColorConvertible defined like this to simplify things :
>> protocol ColorConvertible {
>>   var color: UIColor { get }
>> }
>> 
>> Let's take a more concrete example with the open source library Alamofire (https://github.com/Alamofire/Alamofire <https://github.com/Alamofire/Alamofire>).
>> Alamofire makes an extensive usage of this pattern to convert different types into a "NSURL-compatible" String or a NSURLRequest object.
>> 
>> Then the Alamofire API use only those URLStringConvertible and URLRequestConvertible protocols as method inputs.
>> 
>> It's great because at the right moment where you use the Alamofire API, no matter if you currently use a NSURL or a NSURLComponent, you can pass both as argument to the Alamofire function.
>> 
>> Moreover, you may want to use a custom type to build your URLs and validate them. This allows you to add some safety because you can use strong-typed enums as path components instead of error-prone strings.
>> And here is where this pattern is convenient : you can make your custom type (which could be a class, a struct or an enum) conforming to URLStringConvertible and use it directly as Alamofire API functions input.
>> 
>> But this is sadly limited for Alamofire API. What about all other frameworks which only take NSURL as input ?
>> 
>> Using protocols for this is counterintuitive :
>> 	– protocols are especially thought to don't have to deal with a particular type right ? But what we do here ? We use it only to convert the receiver into the desired type. After the conversion, the original receiver is not even used anymore because we can do nothing with it except convert it.
>> 
>> 	– we already can see different way to write these conversions,  : 
>> 		- var URLString: String { get }  arbitrary var name pattern
>> 		- var integerValue: Int { get }  _Value var name pattern (like in NSNumber)
>> 		- func toColor() -> UIColor  to_() func name pattern 
>> 
>> 	– everytime we want to have a type conversion we need te create the associated protocol but it won't be compatible with third party libraries unless by manually write forwarding methods...
>> 
>> 
>> 2. Swift language already makes convenient bridges between some Obj-C types and theirs Swift counterparts
>> 
>> I didn't take the time to see how it's currently implemented right now, but we already have bridging. For instance this code is valid without doing anything : 
>> 
>> func doSomethingWithNumber(number: NSNumber) {
>>   //...
>> }
>> 
>> let integer: Int = 42
>> doSomethingWithNumber(integer)
>>  
>> Proposed solution
>> 
>> The cleanest way I see to add this feature is by having a new bridge keyword. A bridge could be implemented either into the type implementation scope or in an extension. Bridges can also be a protocol requirement and even have a default implementation thanks to protocol extensions. In fact, bridge keyword can be used in same places than the subscript keyword.
>> 
>> Here is what it could look like for the above NSURL example (note how the bridge could return an optional if needed) :
>> 
>> extension String {
>>   bridge NSURL? {
>>     return NSURL(string: self)
>>   }
>>   
>>   bridge NSURLComponents? {
>>     return NSURLComponents(string: self)
>>   }
>> }
>> 
>> extension NSURLComponents {
>>   bridge NSURL? {
>>     return self.URL
>>   }
>> }
>> 
>> Now this is how Swift-to-Foundation bridge could be implemented with this new syntax : 
>> 
>> extension NSNumber {
>>   bridge Int {
>>     return self.integerValue
>>   }
>> }
>> 
>> extension Int {
>>   bridge NSNumber {
>>     return NSNumber(integerValue: self)
>>   }
>> }
>> 
>> Then, as soon a bridge is defined from a type A to a type B, anywhere an API expects to have a type B, we could pass a type A instead :
>> 
>> enum AppColors {
>>   case PrimaryTextColor
>>   case SecondaryTextColor
>>   
>>   bridge UIColor {
>>     switch self {
>>     case .PrimaryTextColor:
>>       return UIColor.blackColor()
>>     case .SecondaryTextColor:
>>       return UIColor.grayColor()
>>     }
>>   }
>> }
>> 
>> ...
>> 
>> let cell = UITableViewCell(style: .Value1, reuseIdentifier: "MyCell")
>> cell.textLabel?.textColor = .PrimaryTextColor
>> cell.detailTextLabel?.textColor = .SecondaryTextColor
>> 
>> We could also imagine that bridges support error throwing : 
>> 
>> extension String {
>>   enum ColorBridgeError: ErrorType {
>>     case ColorNameNotSupported
>>   }
>>   bridge throws UIColor {
>>     switch self {
>>     case "red":
>>       return UIColor.redColor()
>>     case "blue":
>>       return UIColor.blueColor()
>>     default:
>>       throw ColorBridgeError.ColorNameNotSupported
>>     }
>>   }
>> }
>> 
>> ...
>> 
>> do {
>>   cell.textLabel?.textColor = try self.colorNameTextField.text
>>   self.errorLabel.text = nil
>> }
>> catch String.ColorBridgeError.ColorNameNotSupported {
>>   self.errorLabel.text = "This color name is invalid :("
>> }
>> 
>> This implementation is of course one of many implementations possible and I'm really open to suggestions. For instance I already can see one trade-off of this implementation : for the String -> NSURL? bridge, why NSURL(string: self) would be chosen over NSURL(fileURLWithPath: self) ? 
>> 
>> We could also image than bridge are "chainable" (but maybe it could affect compilation times ?). For instance 
>> 
>> extension A {
>>   bridge B {
>>     return B(withA: self)
>>   }
>> }
>> 
>> extension B {
>>   bridge C {
>>     return C(withB: self)
>>   }
>> }
>> 
>> func doSomethingWithC(anyC: C) {
>>   //...
>> }
>> 
>> let a = A()
>> doSomethingWithC(a) // Compiler could implicitly bridge `a` to type `B`, then bridge the result to type `C`
>> 
>> But this is optional for the feature, we could imagine to explicitly need to implement a bridge from A to C.
>> 
>> 
>> 
>> 
>> Well, I think that's all for now. I hope it was not too long to read and it was well explained. I'm of course open to all suggestions, questions, enhancements, etc...
>> 
>> I would also be happy to have technical details about possible implementations as I'm not an expert at all in compiler design. I really don't know how this feature could affect the compiler and any insight here will be welcome.
>> 
>> Thanks for reading.
>> 
>> Cheers,
>> 
>> Jérôme Alves
>> 
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
> 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160121/8baf048b/attachment.html>


More information about the swift-evolution mailing list