[swift-evolution] [Idea] Use optionals for non-optional parameters

Justin Jia justin.jia.developer at gmail.com
Mon Aug 15 12:05:55 CDT 2016


I agree that being explicit is nice and I also like to use `guard`.

But according to my observation, usually it is easier to make mistakes if we choose to use `guard`.

Let me give you a fake real world example.

With `guard`, you need to be really careful when you want to add new expression (people usually will add to the end of the function).

```
func updateCell(cell: Cell, data: CellData) {
  cell.label.text = data.title
  guard let imageName = data.imageName else { return }
  cell.sublabel.text = cell.humanize(imageName)
  guard let image = UIImage(named: imageName) else { return }
  cell.addBackgroundImage(image)
  // Let's say we changed the design and added a new heading that depends on image name
  cell.heading = String(imageName.characters.first) // This won't be called if image is nil!
}
```

With `if let`, it is really hard to read. This will become more complicated if we add more attributes to cell.

```
func updateCell(cell: Cell, data: CellData) {
  cell.label.text = data.title
  if let imageName = data.imageName {
    cell.sublabel.text = cell.humanize(imageName)
    if let image = UIImage(name: imageName) {
      cell.addBackgroundImage(image)
    }
    cell.heading = String(imageName.characters.first)
  }
}
```

With the proposed syntax:

```
func updateCell(cell: Cell, data: CellData) {
  cell.label.text = data.title
  let imageName = data.imageName // imageName is optional
  cell.sublabel.text = cell.humanize(imageName?)
  let image = UIImage(named: imageName?) // image is optional
  cell.addBackgroundImage(image?)
  cell.heading = String(imageName.characters.first?)
}
```

This is really easy to read. And everything works correctly.


> On Aug 16, 2016, at 12:43 AM, David Rönnqvist <david.ronnqvist at gmail.com> wrote:
> 
>> 
>> On 15 Aug 2016, at 15:29, Justin Jia via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>> 
>> IMO `if x? { }` is not a lot shorter than `if let x = x`.
>> 
>> The problem with `if let` is, you need to explicit specify { } and call the function inside it. It is good for being explicit, but sometimes you ended up with something like this:
>> 
>> ```
>> /* code 1 */
>> if let x = x, let y = y {
>>     / * code 2 */
>>     let z = foo(x, y)
>>     if let z = z {
>>         bar(z)
>>     }
>>     / * code 3 */
>> }
>> /* code 4 */
>> ```
>> 
>> I would like to use guard if possible, but guard will force you to leave the entire function.
>> 
>> ```
>> / * code 1 */
>> guard let x = x, y = y else { return }
>> /* code 2 */
>> / * some code */
>> guard let z = foo(x, y) else { return }
>> bar(z)
>> / * code 3 */ // note: code 3 and code 4 won’t execute if x, y, or z is nil!
>> / * code 4 */ 
>> ```
>> 
>> What I really want is some like this:
>> 
>> ```
>> / * code 1 */
>> let z = foo(x?, y?)
>> / * code 2 */
>> bar(z?)
>> / * code 3 */ // note: code 3 and code 4 will still execute even if z is nil!
>> / * code 4 */
>> ```
>> 
> 
> The fact that this variant and the guard variant doesn’t do the same thing stands out to me. The if-let and guard variants while being more verbose is also very explicit about the control flow. While reading that I can fully understand under what circumstances code 3 and 4 will be executed. This sugar would be more equivalent to this (below), which I’m not sure if everyone would expect it to be. I can see people being surprised that code 3 and 4 was executed, especially if calling `bar` had some side effects that either code 3 or 4 was relying on.
> 
> / * code 1 */
> let z = x.flatMap { 
>   x in y.flatMap { 
>     y in foo(x, y)
>   }
> }
> / * code 2 */
> let _ = z.flatMap { z in bar(z) }
> / * code 3 */ // note: code 3 and code 4 will still execute even if z is nil!
> / * code 4 */
> 
> 
>> IMO, this is much easier to read.
>> 
>> Sincerely,
>> Justin
>> 
>> 
>>> On Aug 15, 2016, at 7:05 PM, Haravikk <swift-evolution at haravikk.me <mailto:swift-evolution at haravikk.me>> wrote:
>>> 
>>> 
>>>> On 15 Aug 2016, at 08:02, Justin Jia via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>>> 
>>>> Hi!
>>>> 
>>>> I don’t know if this has came up before. I tried to search though the mailing list but didn’t find any related threads.
>>>> 
>>>> This is purely a syntactic thing (which I know it’s the lowest priority for Swift 4), but I think it’s an important one.
>>>> 
>>>> Let’s say we have a struct with a function:
>>>> 
>>>> ```
>>>> struct Foo {
>>>>    func bar(x: Int)
>>>> }
>>>> ```
>>>> 
>>>> We can use optionals:
>>>> 
>>>> ```
>>>> let foo: Foo? = nil
>>>> let x = 1
>>>> foo!.bar(x: x) // Able to compile, but will cause runtime error
>>>> foo?.bar(x: x) // Able to compile, and won't cause runtime error
>>>> ```
>>>> 
>>>> However:
>>>> 
>>>> ```
>>>> let foo = Foo()
>>>> let x: Int? = nil
>>>> foo.bar(x: x!) // Able to compile, but will cause runtime error
>>>> foo.bar(x: x?) // Won't compile
>>>> ```
>>>> 
>>>> I propose that we should allow `foo.bar(x: x?)`, which should be equivalent to:
>>>> 
>>>> ```
>>>> if let x = x {
>>>>  foo.bar(x: x)
>>>> }
>>>> ```
>>>> 
>>>> What do you think?
>>> 
>>> I like the intent behind this, but personally I think it's not clear enough. For me, putting the statement in a conditional as you've shown is the better solution, as it's a lot clearer exactly what's going on. Putting a question mark on a variable makes it look like something specific to that variable, rather than preventing the entire statement from executing.
>>> 
>>> There may be some alternatives though, for example, what about a shorthand for the conditional like so:
>>> 
>>> 	if let x? { foo.bar(x: x) }
>>> 	if x? { foo.bar(x: x) } // even shorter?
>>> 
>>> But in general, I think it's best to be explicit about the entire statement being optional, which the conditional does but a postfix on a variable doesn't to the same degree.
>> 
>> _______________________________________________
>> 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/20160816/43515fd8/attachment.html>


More information about the swift-evolution mailing list