[swift-evolution] Brainstorming: Optional sugar inferred map

Paul Ossenbruggen possen at gmail.com
Mon Feb 15 02:49:09 CST 2016


This is a draft.
Sorry for the multi-week delay, been too tired after work to think about this, since it is a long weekend, I continued work on this looking for feedback now. The biggest addition is what to do with parameters to functions by utilizing the nil-coalescing operator in some new ways. I did not finish the detailed design, want to see if it is a good direction first.

Non-Conditional Optional Unwrapping

Proposal: SE- <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md>TBD
Author(s): Paul Ossenbruggen, Craig Cruden 
Status: Awaiting review
Review manager: TBD
 <https://github.com/apple/swift-evolution#introduction>Introduction

Currently there are many cases, in Swift, where control flow must be used to handle optionals. For example

   	let a : Int? = 10
	let b = 5

	if let a = a {
            return a + b
    	} else {
            return nil
    	}

Since we can’t operate on the optional for a, we need to unwrap it, if is non nil otherwise it should return a nil. For something this simple we have introduced control flow statements which makes the intent of the code harder to follow, adds many braces and is less linear. Code that is linear, in general, tends to be easier to follow, because at the end of it you know you have a certain result. 

Swift-evolution thread:  [swift-evolution] Brainstorming: Optional sugar inferred map

 <https://github.com/apple/swift-evolution#motivation>Motivation

Often it is necessary to add conditional code for optionals earlier in the code than you like when it would be nice to leave it as an optional longer, thus allowing your code to be more linear. Optional chaining already allows code to be linearized for properties this proposal builds upon that. 

 <https://github.com/apple/swift-evolution#proposed-solution>Proposed solution

The solution is to allow the question mark operator to be applied to an optional value in places that it currently is prohibited. This already works in Swift 2, for objects. For example: 

var v : String? = "It exists"
var w : String? = nil
v?.appendContentsOf(".")
v -> “It exists."
w?.appendContentsOf(".")
w -> nil

The ? operator is unwrapped and the appendContentsOf method is only executed if the value is non-nil. However you can not do the following, which seems natural:

a? + b

You could write the code from the introduction using the map function, map on an optional will only execute if a is non-nil:

     a.map {$0 + b} 
    
This is pretty non intuitive to someone not familiar with map on optionals, plus they must deal with the closure and an unarmed parameter. Either that or they will have to use the if let form to unwrap. With this proposal, however, you can much more simply and intuitively write it exactly as shown above: a? + b. It is an optional result where the result is nil or the unwrapped a plus b. The code is very easy to understand and it eliminates the control flow. 

One area of complication is if you are using the optional in a parameter to a function that takes a non optional as in the following example:

func doSomething(value: Int) -> Int {
    return value
}
doSomething(a?) // illegal

The question mark would not be allowed as a parameter to a function because the code surrounding the call site should not run if the parameter is nil.

If we were to do this with map, you would do the following:

let c : Int? = nil
let ra = a.map { doSomething($0) }
let rc = c.map { doSomething($0) }

ra -> 5
rc -> nil

To address this, the nil-coalescing operator would allow $$, where $$ is the unwrapped unnamed result of the expression when non nil: 

func doSomething(value: Int) -> Int {
    return value
}

let ra = a ?? doSomething($$) 
let rc = c ?? doSomething($$)

ra -> 5
rc -> nil

multiple statements would allow a statement block after the nil-coalescing operator:

func doSomethingElse(value1: Int, value2: Int) -> Void {
    return value1 + value2
}
let ra = a ?? { 
	  doSomething($$) 
	  doSomethingElse($$, value2: b) 
}
 
multiple optionals would work utilizing a tuple that could address elements using the $n syntax: 

	a? + c? 
	(a, c) ?? doSomethingElse($0, value2: $1)

Using a tuple as opposed to an array would allow disparate types. All values would have to be non-nil for the optional to execute the code after the ??. 
Detailed Design

Continuing from the above example, If a is nil, the rest of the operation will be short circuited, so that if there was a complicated operation to where b is that calculation would not occur. Furthermore, a and b must resolve to the same type when unwrapped. 

<Not Complete>

 <https://github.com/apple/swift-evolution#impact-on-existing-code>Impact on existing code

Existing code would not be affected by this change. 

 <https://github.com/apple/swift-evolution#alternatives-considered>Alternatives considered

Completely implicit unwrapping
The only other alternative considered was Craig Cruden’s initial brainstorming idea of eliminating the need to unwrap it. 

func add(a : Int?, b : Int) -> Int? {
    return a + b
}

This was interesting in that it got the discussion started, but eliminates much of the benefit of optionals, as Thorsten Seitz said:

Too much hidden magic IMO. This would mean losing the benefits of optionals, i.e. making explicit where optional cases occur and that handling the missing case has to be considered. 


Don’t try to solve the parameter cases 
The extensions to the nil-coalescing operator may add complexity to the proposal. Perhaps that should be a separate proposal. This could be useful on its own. 



-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160215/10f8072b/attachment.html>


More information about the swift-evolution mailing list