[swift-evolution] ternary operator ?: suggestion

Craig Cruden ccruden at novafore.com
Tue Jan 19 21:50:06 CST 2016


re - squash for pull request.

> On 2016-01-20, at 8:58:22, Craig Cruden <ccruden at novafore.com> wrote:
> 
> Updated the introduction
> 
> https://github.com/cacruden/swift-evolution/blob/master/proposals/0023-Pattern-Matching-Partial-Function.md <https://github.com/cacruden/swift-evolution/blob/master/proposals/0023-Pattern-Matching-Partial-Function.md>
> 
> You also solved an outstanding issue in my own mind, that of how to implement `collect` like function while defining exhaustive cases.  Where filter would filter existing types, collect would be able to do a similar function… by expecting the closure to return an Optional.
> 
> Something like:
> 
> let arr : [Any] = [5, ‘cats’, 3, ‘goldfish’]
> 
> let arrStr : [String] = arr.collect { 
> 	case let s as String: .Some(s)
>         default: .None
> }
> 
> resulting in [‘cats’, ‘goldfish’]
> 
>> On 2016-01-20, at 6:10:59, Thorsten Seitz <tseitz42 at icloud.com <mailto:tseitz42 at icloud.com>> wrote:
>> 
>> Craig,
>> 
>> thanks! I’m glad you think likewise.
>> 
>> Some first thoughts: what about dropping the second sentence (the one about map) and slightly modifying the third one (dropping the restriction to basic types)? Furthermore I swapped and slightly modified the last two sentences for clarity (I hope):
>> 
>> "Provide the ability for defining partial functions using familiar case/default pattern matching syntax. A new global match function would use this to provide switch-expression functionality for all types. This function would accept a value and a closure containing the case/default partial functions. Combining case/default partial functions in a closure must always be exhaustive providing a total function. Functions on arrays, dictionaries or other collections such as reduce or filter, or rather all higher order functions taking a unary function as argument, would accept these closures as they are just normal closures.“
>> 
>> Actually I’m not sure about introducing the term "partial closure“ because the proposal does not make use of their partiality in any way (like Scala does with the method isDefinedAt() or the chaining methods) and on the contrary requires their combination to be a total function (which is a good thing IMO). As I already wrote in another mail a couple of days before I think in Swift partial functions can simply be formulated by making the return type an optional which I think fits Swift nicely because of its lightweight syntax of optionals.
>> So I don’t think there is a need for partial functions in Swift and therefore I probably would not introduce that term but instead propose a new lightweight syntax for defining unary closures where
>> 
>> { x in 
>>     switch x {
>>     case pattern: return expr
>>     default: return expr
>>     }
>> }
>> 
>> can simply be written as
>> 
>> { 
>>     case pattern: expr
>>     default: expr
>> }
>> 
>> Does that make sense?
>> 
>> -Thorsten
>> 
>> 
>>> Am 19.01.2016 um 19:55 schrieb Craig Cruden <ccruden at novafore.com <mailto:ccruden at novafore.com>>:
>>> 
>>> Thorsten, 
>>> 
>>> I made the changes you suggested with regards to match
>>> 
>>> Can you double check to make sure I did not make any stupid mistakes?
>>> 
>>> 
>>>> On 2016-01-20, at 1:03:16, Thorsten Seitz <tseitz42 at icloud.com <mailto:tseitz42 at icloud.com>> wrote:
>>>> 
>>>> 
>>>>> Am 19.01.2016 um 06:28 schrieb Chris Lattner via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>>:
>>>>> 
>>>>> If you extend the same analogy to switch, then the most important cases are when the pattern being matched and the values being processed are lexically small, and have few cases.  We have a lot of syntactic sugar for processing optionals (e.g. if/let, the ?? operator, etc), but ?? for example doesn’t apply to general pattern matching.  With the expression above, for example, you could match on an enum analogously to the ?? operator like this:
>>>>> 
>>>>> result = someEnum ? case .SomeCase(let x): x, default: y
>>>>> 
>>>>> If you compare that to a switch statement, I can see how that could be compelling.  OTOH, the larger the expression (the more cases) and the more complex the patterns, the better a switch statement starts to look.
>>>> 
>>>> For me the killer feature is not being able to write it inline, it is making it clear that an assignment happens, i.e.
>>>> 
>>>> let result = someEnum ?
>>>>     case .SomeCase(let x): x
>>>>     default: y
>>>> 
>>>> is IMO much more readable (= allowing to instantly see what’s going on) than
>>>> 
>>>> let result
>>>> switch someEnum {
>>>> case .SomeCase(let x): result = x
>>>> default: result = y
>>>> }
>>>> 
>>>> The first one makes the variable which is assigned stand out because the rest is indented and the equals sign is prominently visible.
>>>> The latter version is just a large heap of tokens hiding some assignments :-)
>>>> 
>>>> For the same reason
>>>> 
>>>> let result = match(someEnum) {
>>>>     case .SomeCase(let x): x
>>>>     default: y
>>>> }
>>>> 
>>>> would be just as appealing to me. For using this as a switch-expression I’m strongly against introducing a special map method for basic types, though and rather propose to use a global function „match“ (more on that further below).
>>>> 
>>>> The inline version (if desired) is not too different from the ?-based switch-expression:
>>>> 
>>>> let result = match(someEnum) { case .SomeCase(let x): x; default: y }
>>>> 
>>>> 
>>>> 
>>>> The big advantage of the partial function proposal would be that it is much more general as it is usable everywhere a unary function argument is used, i.e. for map etc.
>>>> With the ?-based switch-expression I would have to write
>>>> 
>>>> let result: [Int] = collection.map { element in element ?
>>>>     case .SomeCase(let x): x
>>>>     default: y
>>>> }
>>>> 
>>>> That is not too bad but requires the boilerplate of having to introduce a name which is instantly consumed and not used anywhere else.
>>>> Admittedly there might be cases (no pun intended) where this might be useful, though, like
>>>> 
>>>> let result: [SomeEnum] = collection.map { element in element ?
>>>>     case .SomeCase(let x) where x > 5: element
>>>>     default: .OtherCase
>>>> }
>>>> 
>>>> Hmmm. Would this use case be common enough to make this version using a ?-based switch-expression be preferable over the partial function syntax?
>>>> 
>>>> 
>>>> 
>>>> As I already said above I’m strongly against introducing a map function for basic types. This brings confusion to the notion of a partial function and to what a map function is and is not needed IMO.
>>>> 
>>>> Just define the following global function:
>>>> 
>>>> func match<T,U>(x: T, @noescape mapping: T -> U) -> U {
>>>>     return mapping(x)
>>>> }
>>>> 
>>>> That’s all that is needed. Except for the case-expression shorthand, of course :-)
>>>> 
>>>> 
>>>> 
>>>> Without that proposed shorthand („partial function“) I currently have to write
>>>> 
>>>> let result = match(someEnum) { (arg: Foo) -> Int in
>>>>     switch arg {
>>>>     case .SomeCase(let x): return x
>>>>     default: return 42
>>>>     }
>>>> }
>>>> 
>>>> instead of the equivalent
>>>> 
>>>> let result = match(someEnum) {
>>>>     case .SomeCase(let x): return x
>>>>     default: return 42
>>>> }
>>>> 
>>>> as proposed (with the added change of using match instead of map).
>>>> 
>>>> 
>>>> -Thorsten
>>>> 
>>>> 
>>> 
>> 
> 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160120/3191fa04/attachment.html>


More information about the swift-evolution mailing list