[swift-evolution] [Pitch] Replace the ternary operator with an in-language function

Austin Zheng austinzheng at gmail.com
Wed Oct 26 00:36:57 CDT 2016


> On Oct 25, 2016, at 10:30 PM, Charlotte Angela Tortorella <charlotte.tortorella at icloud.com> wrote:
> 
>>  Not a replacement for the Swift 4 source stability goal.
> 
> Swift 4 doesn't actually have a source stability goal. It has an ABI stability goal. These are two very different things. ABI is the calling conventions of the language.

It has both.

From https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160725/025676.html <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160725/025676.html>

"For Swift 4, the primary goals are to deliver on the promise of source stability from 3.0 on, and to provide ABI stability for the standard library."

> 
>> So, a programmer learns what '?:' means the first time she encounters it and knows what it does for the rest of her life, can recognize it when she sees it in the many other languages which support it, and can take advantage of its terseness if she chooses. I don't see any downsides.
> 
> It's still confusing to learn and conveys no meaning by virtue of its symbols. Sure, everyone learns what something does and then knows how to use it, that doesn't change that certain things are less intuitive and create a higher barrier of entry to the language until one can be considered "fluent".

Swift is as much a language for professional programmers as it is for learners. The interests of both must be balanced against each other.

> 
>> I don't agree that any of your functions are more readable than "?:"
> 
> A function that explicitly states what is being returned for certain states of the `Bool` are implicitly more readable than nothing at all.

Once you learn what '?:' does, something which is not conceptually difficult by any means, you don't need to be reminded what is being returned.

> 
>> to any significantly greater degree than a `plus()` function would be more readable than `+`
> 
> `+` is a familiar mathematical concept and conveys its meaning to the layperson quite well. `?:` has no such analogue and conveys nothing to the layperson.

'?:' is a familiar concept to anyone who has experience with the C family languages. There are many 'new to Swift' programmers, just as there are many 'new to programming' Swift programmers.

> 
>> `??` is another magic operator, one that has far less prior art than `?:`; why not kill that one first?
> 
> Prior art is no guarantor of quality, case in point `?:`.

Prior art (specifically, familiarity to programmers from C-family languages) is the explicit reason for many, many "sub-optimal" decisions Swift made, like calling ADTs 'enums' and supporting both 'default' and 'case _' as the catchall case in switch statements. You can do a search through the mailing list archives if you want to see Chris Lattner talk about this, although I certainly won't blame you if you don't want to dig through the archives; it's a pity gmane is dead.

> 
> 
>> On 26 Oct. 2016, at 4:20 pm, Austin Zheng <austinzheng at gmail.com <mailto:austinzheng at gmail.com>> wrote:
>> 
>> 
>>> On Oct 25, 2016, at 10:13 PM, Charlotte Angela Tortorella <charlotte.tortorella at icloud.com <mailto:charlotte.tortorella at icloud.com>> wrote:
>>> 
>>> Addressing breaking source code: this is something that an auto migrator could extremely easily be written for.
>> 
>> Auto migrators are:
>> - Specific to Xcode, and therefore OS X Swift development using a very specific toolchain
>> - Less than reliable for large projects
>> - Not a replacement for the Swift 4 source stability goal.
>> 
>>> 
>>> Addressing your first point, `?:` has the advantage of terseness. Your solution requires a lot of code repetition and invariably a programmer will eventually have to deal with `?:` when interacting with literally anyone else's code.
>> 
>> So, a programmer learns what '?:' means the first time she encounters it and knows what it does for the rest of her life, can recognize it when she sees it in the many other languages which support it, and can take advantage of its terseness if she chooses. I don't see any downsides.
>> 
>>> 
>>> Addressing your second point, `?:` has no function signature. Thus we should be comparing at call-site value. A Bool extension that mentions `true` and `false` in the function signature is far more readable than the magical operator of `?:`.
>> 
>> I don't agree that any of your functions are more readable than "?:", to any significantly greater degree than a `plus()` function would be more readable than `+`, but that's probably just a matter of taste. `??` is another magic operator, one that has far less prior art than `?:`; why not kill that one first?
>> 
>>> 
>>> Addressing your "finally", you've completely missed the argument about removing complexity from the compiler, considering how `?:` requires special handling.
>> 
>> I don't think it's a worthwhile tradeoff. We had a proposal a few months back to remove associated type inference, which would have greatly simplified the type checker and fixed a number of critical bugs. It was rejected due to the impact it would have on developer ergonomics. Compiler complexity is not necessarily a more important goal than user experience.
>> 
>>> 
>>>> On 26 Oct. 2016, at 4:04 pm, Austin Zheng <austinzheng at gmail.com <mailto:austinzheng at gmail.com>> wrote:
>>>> 
>>>> Strong -1. I don't feel like stylistic concerns alone are a good enough reason to introduce a change that will undoubtedly break source compatibility for many, many projects come Swift 4.
>>>> 
>>>> That aside, I don't agree with the arguments that the ternary operator is confusing.
>>>> 
>>>> 1. If you don't know that it exists, you aren't hampered in writing code. The most straightforward solution, and a perfectly good one, is to use an if-else to assign a value to either a `var` or a `let`.
>>>> 
>>>> 2. If someone new to Swift thinks `?` and `:` are confusing, I really doubt they will react any better to a generic extension method on a type (which is a primitive in other languages) which takes two "@autoclosure" higher-order function parameters.
>>>> 
>>>> Finally, if you don't find any of the arguments above confusing, why force a breaking change by removing ?: instead of just adding the bool extension, especially given the source stability goals of Swift 4 and beyond?
>>>> 
>>>> Best,
>>>> Austin
>>>> 
>>>>> On Oct 25, 2016, at 9:51 PM, Charlotte Angela Tortorella via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>>>> 
>>>>> Preamble: I've read over the threads that already exist about the ternary operator and to be honest they're a complete mess without a single fully formed proposal.
>>>>> 
>>>>> Pitch: I'd like to simplify the syntax, compiler complexity and learning curve for newcomers when it comes to dealing with the ternary function. The best way to do that, in my opinion, is to remove it entirely and add a new function with better semantics that takes care of ternary operations entirely within the Swift language.
>>>>> 
>>>>> gist: https://gist.github.com/Qata/25a11c21200f1cf8f43ed78e9ffd727c <https://gist.github.com/Qata/25a11c21200f1cf8f43ed78e9ffd727c>
>>>>> 
>>>>> Replace the `?:` operator with an in-language function
>>>>> 
>>>>> Proposal: TBD
>>>>> Author: [Charlotte Tortorella](https://github.com/qata <https://github.com/qata>)
>>>>> Editor: [Soroush Khanlou](https://github.com/khanlou <https://github.com/khanlou>)
>>>>> Review Manager: TBD
>>>>> Status: TBD
>>>>> 
>>>>> Introduction <https://gist.github.com/Qata/25a11c21200f1cf8f43ed78e9ffd727c#introduction>
>>>>> 
>>>>> The ternary operator in Swift was added early in development, as a holdover
>>>>> from C.  This document is an attempt to provide a clear look at the ternary
>>>>> operator without the baggage of the languages that came before, and comes
>>>>> to the conclusion that we should deprecate and remove the ternary operator
>>>>> in favor of an extension to `Bool`.
>>>>> 
>>>>> As a quick refresher, here's what the ternary operator looks like:
>>>>> 
>>>>> let a = 10
>>>>> let b = 20
>>>>> // If a is less than b, sets e to "foo", else sets e to "bar"
>>>>> let e = a < b ? "foo" : "bar"
>>>>> 
>>>>> Advantages of The Ternary Operator <https://gist.github.com/Qata/25a11c21200f1cf8f43ed78e9ffd727c#advantages-of-the-ternary-operator>
>>>>> 
>>>>> The primary advantage of this operator is its terseness and expressive
>>>>> capability. It's shorthand for (e.g.):
>>>>> 
>>>>> let a = 10
>>>>> let b = 20
>>>>> let e: String
>>>>> if a < b {
>>>>>   e = "foo"
>>>>> } else {
>>>>>   e = "bar"
>>>>> }
>>>>> 
>>>>> The second advantage of Swift supporting the ternary operator is continuity
>>>>> with C, and other common languages in the extended C family (C++, Objective-C,
>>>>> Java, C#, Javascript, etc).  People coming to Swift from these other languages
>>>>> may reasonably expect this operator to exist.  That said, there are also
>>>>> popular languages which have kept the majority of C operators but dropped the
>>>>> ternary operator (e.g. [Go](https://golang.org/doc/faq#Does_Go_have_a_ternary_form <https://golang.org/doc/faq#Does_Go_have_a_ternary_form>) and [Rust](https://github.com/rust-lang/rfcs/issues/1362 <https://github.com/rust-lang/rfcs/issues/1362>)).
>>>>> 
>>>>> 
>>>>> Disadvantages of The Ternary Operator <https://gist.github.com/Qata/25a11c21200f1cf8f43ed78e9ffd727c#disadvantages-of-the-ternary-operator>
>>>>> 
>>>>> 1. The existence of the ternary operator as a holdover from C is to increase
>>>>> the familiarity of the Swift language for C family developers, at the expense
>>>>> of newcomers.  Established developers do much better with learning concepts
>>>>> than newcomers to programming and probably don't need their hands held
>>>>> with this carry over of an operator.
>>>>> 
>>>>> 2. The ternary operator adds complexity to the compiler, because it requires
>>>>> special handling.  It is the only operator that requires two components to
>>>>> work (both the `?` and the `:`), it uses a character that is excluded from
>>>>> being used in other operators (`:`), and it isn't defined in the standard
>>>>> library.
>>>>> 
>>>>> 3. The ternary operator's usage of `?` can be confusing
>>>>> to new users.  Every other instance of `?` is associated with
>>>>> `Optional` values.
>>>>> 
>>>>> 4. The ternary operator uses `:`, which is already a heavily overloaded
>>>>> symbol in Swift.  `:` is used in hash tables, type annotations for variables,
>>>>> class inheritance, and protocol conformance.
>>>>> 
>>>>> 5. The ternary operator's short length lends it to being abused in the
>>>>> nested ternary operator anti-pattern.  This is similar to the `++` and
>>>>> `--` operators, which were removed in Swift 3.  While they worked fine and were
>>>>> readable enough when used alone, using them multiple times in a single
>>>>> expression like `function(a++, ++a)` made them highly unreadable and
>>>>> confusing.
>>>>> 
>>>>> 6. This operator is only applicable to a single type, `Bool`.
>>>>> 
>>>>> 7. If the ternary operator weren't in common usage, it would not be proposed
>>>>> for Swift.  Higher clarity can be achieved with common language features by
>>>>> creating an extension to `Bool`.
>>>>> 
>>>>> 8. The ternary operator was created for and is much more suited to a language
>>>>> like C, where there were no generics and as such no alternative to an
>>>>> unintuitive operator.
>>>>> 
>>>>> 9. Several other modern languages, like Rust and Go discussed earlier, have
>>>>> eschewed the usage of the ternary operator entirely.  Other languages that have
>>>>> special constructs similar to `?:`, such as `if then else` in Haskell have
>>>>> [discussed removing it](https://wiki.haskell.org/If-then-else#Is_If-Then-Else_so_important.3F <https://wiki.haskell.org/If-then-else#Is_If-Then-Else_so_important.3F>).  `if then else` is identical to the `?:` operator,
>>>>> excepting that it's prefixed by `if`, while `?:` has no prefix.
>>>>> 
>>>>>  Example: `if True then 10 else 20`
>>>>> 
>>>>> 10. On a more personal and anecdotal note, the ternary operator gave me more
>>>>> trouble than any other operator when I was first learning how to program.
>>>>> I’ve also spoken to several other people who expressed similar sentiments
>>>>> about this operator’s inscrutability.
>>>>> 
>>>>> Proposed Approach <https://gist.github.com/Qata/25a11c21200f1cf8f43ed78e9ffd727c#proposed-approach>
>>>>> 
>>>>> We should drop the ternary operator in favor of a new extension to `Bool`.
>>>>> There are a few possibilities for the naming of this function.  We've provided
>>>>> four for consideration in this proposal, but are open to other options as well.
>>>>> This proposal is much more about the concept than the naming of the replacement
>>>>> function.
>>>>> 
>>>>> extension Bool {
>>>>>     /// If `self == true`, returns `t`, otherwise, returns `f`.
>>>>>     func transformed<T>(true t: @autoclosure () -> T, false f: @autoclosure () -> T) -> T {
>>>>>         if self {
>>>>>             return t()
>>>>>         } else {
>>>>>             return f()  
>>>>>         }
>>>>>     }
>>>>> 
>>>>>     func when<T>(true t: @autoclosure () -> T, false f: @autoclosure () -> T) -> T {
>>>>>       ...
>>>>>     }
>>>>> 
>>>>>     func if<T>(true t: @autoclosure () -> T, false f: @autoclosure () -> T) -> T {
>>>>>       ...
>>>>>     }
>>>>> 
>>>>>     func if<T>(then t: @autoclosure () -> T, else f: @autoclosure () -> T) -> T {
>>>>>       ...
>>>>>     }
>>>>> }
>>>>> 
>>>>> Only one of these should be chosen.  We're not proposing adding multiple
>>>>> functions that achieve the same thing.
>>>>> 
>>>>> Example usage:
>>>>> 
>>>>> let a = 10
>>>>> let b = 20
>>>>> _ = (a < b).transformed(true: "foo", false: "bar")
>>>>> _ = (a < b).when(true: "foo", false: "bar")
>>>>> _ = (a < b).if(true: "foo", false: "bar")
>>>>> _ = (a < b).if(then: "foo", else: "bar")
>>>>> 
>>>>> Impact on existing code <https://gist.github.com/Qata/25a11c21200f1cf8f43ed78e9ffd727c#impact-on-existing-code>
>>>>> 
>>>>> This proposal is breaking and would require migration.
>>>>> 
>>>>> Alternatives considered <https://gist.github.com/Qata/25a11c21200f1cf8f43ed78e9ffd727c#alternatives-considered>
>>>>> 
>>>>> Simplest alternative: we could leave the ternary operator as is and not
>>>>> introduce any new concepts.
>>>>> 
>>>>> It'd also be possible to add an `if then else` Haskell-esque expression.
>>>>> This would have the disadvantages of still needing special handling by the
>>>>> compiler.  Since this proposal's intention is partially to remove compiler
>>>>> complexity, this would be counterproductive and would probably confuse new
>>>>> users in a similar way to how `?:` does.
>>>>> 
>>>>> _______________________________________________
>>>>> 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/20161025/611dc5fb/attachment.html>


More information about the swift-evolution mailing list