[swift-evolution] [Proposal] Built-in "bridge" feature
Javier Soto
javier.api at gmail.com
Thu Jan 21 00:58:19 CST 2016
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 at 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 at 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 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> 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).
> 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
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
--
Javier Soto
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160121/0cd5a8b9/attachment.html>
More information about the swift-evolution
mailing list