[Proposal] Built-in "bridge" feature


(Jerome ALVES) #1

Hi everyone,

This is my first message on the mailing list and I hope I'll do everything right :slight_smile:
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).
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


(David Sweeris) #2

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@swift.org> wrote:

Hi everyone,

This is my first message on the mailing list and I hope I'll do everything right :slight_smile:
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).
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@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Jerome ALVES) #3

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@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@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi everyone,

This is my first message on the mailing list and I hope I'll do everything right :slight_smile:
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).
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@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(David Sweeris) #4

I was thinking A as C would tell the compiler to try A bridge C, and failing that, look through the types to which A *can* bridge to see if any of them can bridge to C, and so on‚Ķ Having thought about it more, though, that‚Äôs a horrible idea. It‚Äôd be way too easy to have some *very* subtle side effects if you bridge YourAwesomeType between a couple of types that were previously bridged by some other path (a change in rounding behavior comes to mind). This is especially true since there‚Äôs nothing preventing the bridges from being ‚Äúlossy‚Ä̂Ķ imagine ‚Äúbridging‚ÄĚ 0.0001 to YourAwesomeType (which stores it as an Int) before then bridging to some other type, where previously 0.0001 would‚Äôve gotten there via some path that *didn‚Äôt* round it to an Int along the way.

Yeah, probably stick with having to be explicit about *how* you intend A to bridge to C, if it can’t just do it directly

- Dave Sweeris

···

On Jan 20, 2016, at 15:49, Jerome ALVES <j.alves@me.com> wrote:

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@mac.com <mailto:davesweeris@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@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi everyone,

This is my first message on the mailing list and I hope I'll do everything right :slight_smile:
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).
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@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Javier Soto) #5

IIRC there was syntax to do this in Swift 1.0 which latter got removed from
the language.
This sort of feature enables some cool patterns. I remember an example
where some sort of Synchronized<T> object could be passed to a function
expecting a T using this feature. However I can also see how implicit
conversions can make code hard to read, and maybe they also cause
complexity in the compiler (where suddenly every expression could
potentially be evaluated as another type)

···

On Wed, Jan 20, 2016 at 4:28 PM Dave via swift-evolution < swift-evolution@swift.org> wrote:

I was thinking A as C would tell the compiler to try A bridge C, and
failing that, look through the types to which A *can* bridge to see if any
of them can bridge to C, and so on… Having thought about it more, though,
that’s a horrible idea. It’d be way too easy to have some *very* subtle
side effects if you bridge YourAwesomeType between a couple of types that
were previously bridged by some other path (a change in rounding behavior
comes to mind). This is especially true since there’s nothing preventing
the bridges from being ‚Äúlossy‚Ä̂Ķ imagine ‚Äúbridging‚ÄĚ 0.0001
to YourAwesomeType (which stores it as an Int) before then bridging to some
other type, where previously 0.0001 would’ve gotten there via some path
that *didn’t* round it to an Int along the way.

Yeah, probably stick with having to be explicit about *how* you intend A
to bridge to C, if it can’t just do it directly

- Dave Sweeris

On Jan 20, 2016, at 15:49, Jerome ALVES <j.alves@me.com> wrote:

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@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@swift.org> wrote:

Hi everyone,

This is my first message on the mailing list and I hope I'll do everything
right :slight_smile:
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).
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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
Javier Soto


(Austin Zheng) #6

I don't really like this feature; I see it as one of those "more brevity in
exchange for less clarity" features that are an anti-goal of the project.
If a bug is caused by a type conversion, I want to be able to narrow the
offending conversion to an explicit call, not have to guess at which point
the compiler decided to insert a conversion, which conversion was inserted,
or whether one of my dependencies might be exporting a conversion I wasn't
aware of.

There was also a thread some time ago where one of the developers stated
that adding general implicit conversions to Swift would make building a
performant type checker impossible, but I can't find it.

One major exception I can think of is QoL issues when working with numbers
and arithmetic, for example automatically widening integers, but there's a
separate project working on redesigning that aspect of the language.

Best,
Austin

···

On Wed, Jan 20, 2016 at 10:58 PM, Javier Soto via swift-evolution < swift-evolution@swift.org> wrote:

IIRC there was syntax to do this in Swift 1.0 which latter got removed
from the language.
This sort of feature enables some cool patterns. I remember an example
where some sort of Synchronized<T> object could be passed to a function
expecting a T using this feature. However I can also see how implicit
conversions can make code hard to read, and maybe they also cause
complexity in the compiler (where suddenly every expression could
potentially be evaluated as another type)

On Wed, Jan 20, 2016 at 4:28 PM Dave via swift-evolution < > swift-evolution@swift.org> wrote:

I was thinking A as C would tell the compiler to try A bridge C, and
failing that, look through the types to which A *can* bridge to see if any
of them can bridge to C, and so on… Having thought about it more, though,
that’s a horrible idea. It’d be way too easy to have some *very* subtle
side effects if you bridge YourAwesomeType between a couple of types that
were previously bridged by some other path (a change in rounding behavior
comes to mind). This is especially true since there’s nothing preventing
the bridges from being ‚Äúlossy‚Ä̂Ķ imagine ‚Äúbridging‚ÄĚ 0.0001
to YourAwesomeType (which stores it as an Int) before then bridging to some
other type, where previously 0.0001 would’ve gotten there via some path
that *didn’t* round it to an Int along the way.

Yeah, probably stick with having to be explicit about *how* you intend A
to bridge to C, if it can’t just do it directly

- Dave Sweeris

On Jan 20, 2016, at 15:49, Jerome ALVES <j.alves@me.com> wrote:

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@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@swift.org> wrote:

Hi everyone,

This is my first message on the mailing list and I hope I'll do
everything right :slight_smile:
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).
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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
Javier Soto
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Jerome ALVES) #7

Well, If you think about it, this bridge feature how I presented it here does nothing more than what we can do today. Like error handling in Swift 2, it's just a syntactic sugar to standardize a commonly used pattern. Moreover if we decide to don't allow implicit conversion but only explicit conversions with the "as" keyword, we have a feature which increase clarity.

Don't you find that in...

func doSomethingWith(url: NSURL) {
    ...
}

let urlString = "http://swift.org"

... this syntax :
doSomethingWith(urlString as! NSURL) // we use as! instead of as because this bridge returns a NSURL? instead of NSURL

... is more clear than this syntax :
doSomethingWith(urlString.toURL()!)

Moreover, the IDE can bring some useful features here : We can make a cmd + click on the "as" keyword to jump right into the bridge implementation, or a option + click to see the doc associated to it.

- J√©r√īme

···

Le 21 janv. 2016 à 08:35, Austin Zheng <austinzheng@gmail.com> a écrit :

I don't really like this feature; I see it as one of those "more brevity in exchange for less clarity" features that are an anti-goal of the project. If a bug is caused by a type conversion, I want to be able to narrow the offending conversion to an explicit call, not have to guess at which point the compiler decided to insert a conversion, which conversion was inserted, or whether one of my dependencies might be exporting a conversion I wasn't aware of.

There was also a thread some time ago where one of the developers stated that adding general implicit conversions to Swift would make building a performant type checker impossible, but I can't find it.

One major exception I can think of is QoL issues when working with numbers and arithmetic, for example automatically widening integers, but there's a separate project working on redesigning that aspect of the language.

Best,
Austin

On Wed, Jan 20, 2016 at 10:58 PM, Javier Soto via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
IIRC there was syntax to do this in Swift 1.0 which latter got removed from the language.
This sort of feature enables some cool patterns. I remember an example where some sort of Synchronized<T> object could be passed to a function expecting a T using this feature. However I can also see how implicit conversions can make code hard to read, and maybe they also cause complexity in the compiler (where suddenly every expression could potentially be evaluated as another type)

On Wed, Jan 20, 2016 at 4:28 PM Dave via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
I was thinking A as C would tell the compiler to try A bridge C, and failing that, look through the types to which A *can* bridge to see if any of them can bridge to C, and so on‚Ķ Having thought about it more, though, that‚Äôs a horrible idea. It‚Äôd be way too easy to have some *very* subtle side effects if you bridge YourAwesomeType between a couple of types that were previously bridged by some other path (a change in rounding behavior comes to mind). This is especially true since there‚Äôs nothing preventing the bridges from being ‚Äúlossy‚Ä̂Ķ imagine ‚Äúbridging‚ÄĚ 0.0001 to YourAwesomeType (which stores it as an Int) before then bridging to some other type, where previously 0.0001 would‚Äôve gotten there via some path that *didn‚Äôt* round it to an Int along the way.

Yeah, probably stick with having to be explicit about *how* you intend A to bridge to C, if it can’t just do it directly

- Dave Sweeris

On Jan 20, 2016, at 15:49, Jerome ALVES <j.alves@me.com <mailto:j.alves@me.com>> wrote:

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@mac.com <mailto:davesweeris@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@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi everyone,

This is my first message on the mailing list and I hope I'll do everything right :slight_smile:
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).
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@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution
--
Javier Soto
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Austin Zheng) #8

I wouldn't be opposed to this if the 'as' was required (or there were some other syntax that prevented it from being implicit).

There are a few more questions that come up, but they have more to do with the specifics of the implementation:

- How would the compiler use the syntax to distinguish between different valid conversions (you briefly mentioned this)? What about conversions that you import from a different module? Would it be too limiting to restrict this to conversions that are defined in the same module (+ any in the stdlib)?
- How would the 'as' syntax (or even the non-'as' syntax) work with generic types? For example, Javi mentioned a possibly useful conversion from T --> Synchronized<T>. (Side note - might this be a use case for higher-kinded types?)

Best,
Austin

···

On Jan 21, 2016, at 7:05 AM, Jerome ALVES <j.alves@me.com> wrote:

Well, If you think about it, this bridge feature how I presented it here does nothing more than what we can do today. Like error handling in Swift 2, it's just a syntactic sugar to standardize a commonly used pattern. Moreover if we decide to don't allow implicit conversion but only explicit conversions with the "as" keyword, we have a feature which increase clarity.

Don't you find that in...

func doSomethingWith(url: NSURL) {
    ...
}

let urlString = "http://swift.org <http://swift.org/>"

... this syntax :
doSomethingWith(urlString as! NSURL) // we use as! instead of as because this bridge returns a NSURL? instead of NSURL

... is more clear than this syntax :
doSomethingWith(urlString.toURL()!)

Moreover, the IDE can bring some useful features here : We can make a cmd + click on the "as" keyword to jump right into the bridge implementation, or a option + click to see the doc associated to it.

- J√©r√īme

Le 21 janv. 2016 à 08:35, Austin Zheng <austinzheng@gmail.com <mailto:austinzheng@gmail.com>> a écrit :

I don't really like this feature; I see it as one of those "more brevity in exchange for less clarity" features that are an anti-goal of the project. If a bug is caused by a type conversion, I want to be able to narrow the offending conversion to an explicit call, not have to guess at which point the compiler decided to insert a conversion, which conversion was inserted, or whether one of my dependencies might be exporting a conversion I wasn't aware of.

There was also a thread some time ago where one of the developers stated that adding general implicit conversions to Swift would make building a performant type checker impossible, but I can't find it.

One major exception I can think of is QoL issues when working with numbers and arithmetic, for example automatically widening integers, but there's a separate project working on redesigning that aspect of the language.

Best,
Austin

On Wed, Jan 20, 2016 at 10:58 PM, Javier Soto via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
IIRC there was syntax to do this in Swift 1.0 which latter got removed from the language.
This sort of feature enables some cool patterns. I remember an example where some sort of Synchronized<T> object could be passed to a function expecting a T using this feature. However I can also see how implicit conversions can make code hard to read, and maybe they also cause complexity in the compiler (where suddenly every expression could potentially be evaluated as another type)

On Wed, Jan 20, 2016 at 4:28 PM Dave via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
I was thinking A as C would tell the compiler to try A bridge C, and failing that, look through the types to which A *can* bridge to see if any of them can bridge to C, and so on‚Ķ Having thought about it more, though, that‚Äôs a horrible idea. It‚Äôd be way too easy to have some *very* subtle side effects if you bridge YourAwesomeType between a couple of types that were previously bridged by some other path (a change in rounding behavior comes to mind). This is especially true since there‚Äôs nothing preventing the bridges from being ‚Äúlossy‚Ä̂Ķ imagine ‚Äúbridging‚ÄĚ 0.0001 to YourAwesomeType (which stores it as an Int) before then bridging to some other type, where previously 0.0001 would‚Äôve gotten there via some path that *didn‚Äôt* round it to an Int along the way.

Yeah, probably stick with having to be explicit about *how* you intend A to bridge to C, if it can’t just do it directly

- Dave Sweeris

On Jan 20, 2016, at 15:49, Jerome ALVES <j.alves@me.com <mailto:j.alves@me.com>> wrote:

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@mac.com <mailto:davesweeris@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@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi everyone,

This is my first message on the mailing list and I hope I'll do everything right :slight_smile:
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).
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@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution
--
Javier Soto
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution