[Idea] Use optionals for non-optional parameters


(Justin Jia) #1

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?

Thanks,
Justin


SE-0231 — Optional iteration
(Xiaodi Wu) #2

It has been mentioned before (more than once, perhaps, but not in its own
thread I don't think, so good luck finding it). IIRC, one of the problems
is that it's unclear what happens if your function takes multiple
arguments. Does evaluation proceed from left to right? does it
short-circuit? Put concretely:

func bar(_ x: Int) -> Int? { /* side effects */ }
func baz(_ y: Int) -> Int? { /* side effects */ }
func foo(_ z: Int, _ a: Int) -> Int { /* ... */ }

print(foo(bar(42)?, baz(42)?))

Does baz(42) get evaluated if bar returns nil? Does bar(42) get evaluated
if baz returns nil?

···

On Mon, Aug 15, 2016 at 2:02 AM, Justin Jia via swift-evolution < swift-evolution@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?

Thanks,
Justin

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Charlie Monroe) #3

Hi Justin,

IIRC, there were several discussions about this, all of them ending up at a dead point. The question was, what would be the behavior of

foo(argA: methodA(), argB: methodB())

where methodA returns an optional. If that evaluates to nil, would methodB get evaluated as well? Or should it shortcircuit?

···

On Aug 15, 2016, at 9:02 AM, Justin Jia via swift-evolution <swift-evolution@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?

Thanks,
Justin

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Tino) #4

Yes, those guards and ifs can be tedious, but it is clear what they are doing, and there is no doubt what code will be evaluated and what will be skipped.
If Swift had syntactic sugar that acts like optional chaining for parameters, I don't think that I would use it, because by moving the question mark far to the right, it can easily hide that something is conditional.

···

Am 15.08.2016 um 09:02 schrieb Justin Jia via swift-evolution <swift-evolution@swift.org>:

foo.bar(x: x?)


(Haravikk) #5

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.

···

On 15 Aug 2016, at 08:02, Justin Jia via swift-evolution <swift-evolution@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?


(Justin Jia) #6

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 */

IMO, this is much easier to read.

Sincerely,
Justin

···

On Aug 15, 2016, at 7:05 PM, Haravikk <swift-evolution@haravikk.me> wrote:

On 15 Aug 2016, at 08:02, Justin Jia via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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.


(Tim Vermeulen) #7

> 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.

I get where you’re coming from, but how would people react if optional chaining wasn’t in the language yet and someone proposed it now? I know it’s not strictly the same thing, but it’s still a single question mark that prevents the whole statement from being executed. I think it would be met with a lot of resistance from people saying that being more explicit with `if let` is the way to go.

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?

The alternatives you’ve come up with would only work if foo.bar doesn’t return anything. If it does return something, and you want to assign it to a variable, you have to declare the variable beforehand and it just becomes ugly. It’s then probably a better idea to use map/flatmap:

x.flatMap { foo.bar(x: $0) }

But IMO that's not as clean as `foo.bar(x: x?)`. flatMap is so hidden to most people that they resort to things like

myDictionary[myOptionalString ?? “”]

assuming that no key is the empty string. People might also make function overloads that accept and return optionals, just to avoid nesting. I’m sure you’ve seen something like this before:

if limit == nil || number < limit! { /* … */ }

There’s no great way to “fix” this. We can do

if limit.map({ number < $0 }) { /* … */ }

or we can overload the < operator for optionals to return Bool?, which we can use with

if (number < limit) ?? true { /* … */ }

With the new sugar, we’d be able to do

if (number < limit?) ?? true { /* … */ }

out of the box.

···

> On 15 Aug 2016, at 08:02, Justin Jia via swift-evolution<swift-evolution@swift.org(mailto:swift-evolution@swift.org)>wrote:

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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Justin Jia) #8

I think the `?` will make the return an optional. So your example should add another ? at the end.

print(foo(bar(42)?, baz(42)?)?)

The above example should be equivalent to:

let x = bar(42) // X is optional
let y = baz(42) // Y is optional

let z = foo(x?, y?) // Z should be optional now
print(z?)

which should be equivalent to;

let x = bar(42)
let y = baz(42)

if let x = x, let y = y {
    z = foo(x, y)
}

if let z = z {
    print(z)
}

We don’t need to worry about “short-circuit” now because it should be equivalent to the above syntax.

···

It has been mentioned before (more than once, perhaps, but not in its own thread I don't think, so good luck finding it). IIRC, one of the problems is that it's unclear what happens if your function takes multiple arguments. Does evaluation proceed from left to right? does it short-circuit? Put concretely:

func bar(_ x: Int) ->Int? { /* side effects */ }
func baz(_ y: Int) ->Int? { /* side effects */ }
func foo(_ z: Int, _ a: Int) ->Int { /* ... */ }

print(foo(bar(42)?, baz(42)?))

Does baz(42) get evaluated if bar returns nil? Does bar(42) get evaluated if baz returns nil?

On Mon, Aug 15, 2016 at 2:02 AM, Justin Jia via swift-evolution<swift-evolution@swift.org(mailto:swift-evolution@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?
>
> Thanks,
> Justin
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org(mailto:swift-evolution@swift.org)
> https://lists.swift.org/mailman/listinfo/swift-evolution


(David Rönnqvist) #9

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 */

···

On 15 Aug 2016, at 15:29, Justin Jia via swift-evolution <swift-evolution@swift.org> wrote:

IMO, this is much easier to read.

Sincerely,
Justin

On Aug 15, 2016, at 7:05 PM, Haravikk <swift-evolution@haravikk.me <mailto:swift-evolution@haravikk.me>> wrote:

On 15 Aug 2016, at 08:02, Justin Jia via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Justin Jia) #10

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@gmail.com> wrote:

On 15 Aug 2016, at 15:29, Justin Jia via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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@haravikk.me <mailto:swift-evolution@haravikk.me>> wrote:

On 15 Aug 2016, at 08:02, Justin Jia via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Charlie Monroe) #11

I think the `?` will make the return an optional. So your example should add another ? at the end.

print(foo(bar(42)?, baz(42)?)?)

This reads terribly, IMHO.


The above example should be equivalent to:

let x = bar(42) // X is optional
let y = baz(42) // Y is optional

let z = foo(x?, y?) // Z should be optional now
print(z?)


which should be equivalent to;

\`\`\`
let x = bar\(42\)
let y = baz\(42\)

if let x = x, let y = y \{
&nbsp;&nbsp;&nbsp;&nbsp;z = foo\(x, y\)
\}

What if baz doesn't return optional?

if let x = bar(42) {
  let y = baz(42)
  z = foo(x, y)
}

or

let y = baz(42)
if let x = bar(42) {
  z = foo(x, y)
}

If both are evaluated, this is really inconsistent with

if let x = bar(42), y = baz(42) { ... }

which will short-circuit and baz will not be evaluated if x is evaluated as nil.

···

On Aug 15, 2016, at 9:46 AM, Justin Jia via swift-evolution <swift-evolution@swift.org> wrote:

if let z = z {
    print(z)
}
```

We don’t need to worry about “short-circuit” now because it should be equivalent to the above syntax.

> It has been mentioned before (more than once, perhaps, but not in its own thread I don't think, so good luck finding it). IIRC, one of the problems is that it's unclear what happens if your function takes multiple arguments. Does evaluation proceed from left to right? does it short-circuit? Put concretely:
>
> ```
> func bar(_ x: Int) ->Int? { /* side effects */ }
> func baz(_ y: Int) ->Int? { /* side effects */ }
> func foo(_ z: Int, _ a: Int) ->Int { /* ... */ }
>
> print(foo(bar(42)?, baz(42)?))
> ```
>
> Does baz(42) get evaluated if bar returns nil? Does bar(42) get evaluated if baz returns nil?
>
>
> On Mon, Aug 15, 2016 at 2:02 AM, Justin Jia via swift-evolution<swift-evolution@swift.org <mailto:swift-evolution@swift.org>(mailto:swift-evolution@swift.org <mailto:swift-evolution@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?
> >
> > Thanks,
> > Justin
> >
> > _______________________________________________
> > swift-evolution mailing list
> > swift-evolution@swift.org <mailto:swift-evolution@swift.org>(mailto:swift-evolution@swift.org <mailto:swift-evolution@swift.org>)
> > https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Justin Jia) #12

I think the `?` will make the return an optional. So your example should add another ? at the end.

print(foo(bar(42)?, baz(42)?)?)

This reads terribly, IMHO.

I agree that this reads terribly. But this is only an edge case. I would prefer let output = foo(bar(42)?, baz(42)?); print(output?). However, I want to argue that for most cases “?” reads better than “if let”.


The above example should be equivalent to:

let x = bar(42) // X is optional
let y = baz(42) // Y is optional

let z = foo(x?, y?) // Z should be optional now
print(z?)


which should be equivalent to;

let x = bar(42)
let y = baz(42)

if let x = x, let y = y {
z = foo(x, y)
}

What if baz doesn’t return optional?

If baz doesn’t return optional, then we don’t even need this syntax:

We can just write: z(bar(42)?, baz(42) and treat baz(42) like normal.

Which should be equivalent to:

let x = bar(42)
let y = baz(42)

if let x = x {
z = foo(x, y)
}


<details class='elided'>
<summary title='Show trimmed content'>&#183;&#183;&#183;</summary>

> On Aug 15, 2016, at 3:53 PM, Charlie Monroe &lt;charlie@charliemonroe\.net&gt; wrote:
>> On Aug 15, 2016, at 9:46 AM, Justin Jia via swift\-evolution &lt;swift\-evolution@swift\.org &lt;mailto:swift\-evolution@swift\.org&gt;&gt; wrote:

> if let x = bar\(42\) \{
> &nbsp;&nbsp;let y = baz\(42\)
> &nbsp;&nbsp;z = foo\(x, y\)
> \}
>
> or 
>
> let y = baz\(42\)
> if let x = bar\(42\) \{
> &nbsp;&nbsp;z = foo\(x, y\)
> \}
>
> If both are evaluated, this is really inconsistent with
>
> if let x = bar\(42\), y = baz\(42\) \{ \.\.\. \}
>
> which will short\-circuit and baz will not be evaluated if x is evaluated as nil\.
>
>> if let z = z \{
>> &nbsp;&nbsp;&nbsp;&nbsp;print\(z\)
>> \}
>> \`\`\`
>>
>> We don’t need to worry about “short\-circuit” now because it should be equivalent to the above syntax\.
>>
>> &gt; It has been mentioned before \(more than once, perhaps, but not in its own thread I don&#39;t think, so good luck finding it\)\. IIRC, one of the problems is that it&#39;s unclear what happens if your function takes multiple arguments\. Does evaluation proceed from left to right? does it short\-circuit? Put concretely:
>> &gt; 
>> &gt; \`\`\`
>> &gt; func bar\(\_ x: Int\) \-&gt;Int? \{ /\* side effects \*/ \}
>> &gt; func baz\(\_ y: Int\) \-&gt;Int? \{ /\* side effects \*/ \}
>> &gt; func foo\(\_ z: Int, \_ a: Int\) \-&gt;Int \{ /\* \.\.\. \*/ \}
>> &gt; 
>> &gt; print\(foo\(bar\(42\)?, baz\(42\)?\)\)
>> &gt; \`\`\`
>> &gt; 
>> &gt; Does baz\(42\) get evaluated if bar returns nil? Does bar\(42\) get evaluated if baz returns nil?
>> &gt; 
>> &gt; 
>> &gt; On Mon, Aug 15, 2016 at 2:02 AM, Justin Jia via swift\-evolution&lt;swift\-evolution@swift\.org &lt;mailto:swift\-evolution@swift\.org&gt;\(mailto:swift\-evolution@swift\.org &lt;mailto:swift\-evolution@swift\.org&gt;\)&gt;wrote:
>> &gt; &gt; Hi\!
>> &gt; &gt; 
>> &gt; &gt; 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\.
>> &gt; &gt; 
>> &gt; &gt; 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\.
>> &gt; &gt; 
>> &gt; &gt; Let’s say we have a struct with a function:
>> &gt; &gt; 
>> &gt; &gt; \`\`\`
>> &gt; &gt; struct Foo \{
>> &gt; &gt; func bar\(x: Int\)
>> &gt; &gt; \}
>> &gt; &gt; \`\`\`
>> &gt; &gt; 
>> &gt; &gt; We can use optionals:
>> &gt; &gt; 
>> &gt; &gt; \`\`\`
>> &gt; &gt; let foo: Foo? = nil
>> &gt; &gt; let x = 1
>> &gt; &gt; foo\!\.bar\(x: x\) // Able to compile, but will cause runtime error
>> &gt; &gt; foo?\.bar\(x: x\) // Able to compile, and won&#39;t cause runtime error
>> &gt; &gt; \`\`\`
>> &gt; &gt; 
>> &gt; &gt; However:
>> &gt; &gt; 
>> &gt; &gt; \`\`\`
>> &gt; &gt; let foo = Foo\(\)
>> &gt; &gt; let x: Int? = nil
>> &gt; &gt; foo\.bar\(x: x\!\) // Able to compile, but will cause runtime error
>> &gt; &gt; foo\.bar\(x: x?\) // Won&#39;t compile
>> &gt; &gt; \`\`\`
>> &gt; &gt; 
>> &gt; &gt; I propose that we should allow \`foo\.bar\(x: x?\)\`, which should be equivalent to:
>> &gt; &gt; 
>> &gt; &gt; \`\`\`
>> &gt; &gt; if let x = x \{
>> &gt; &gt; foo\.bar\(x: x\)
>> &gt; &gt; \}
>> &gt; &gt; \`\`\`
>> &gt; &gt; 
>> &gt; &gt; What do you think?
>> &gt; &gt; 
>> &gt; &gt; Thanks,
>> &gt; &gt; Justin
>> &gt; &gt; 
>> &gt; &gt; \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_
>> &gt; &gt; swift\-evolution mailing list
>> &gt; &gt; swift\-evolution@swift\.org &lt;mailto:swift\-evolution@swift\.org&gt;\(mailto:swift\-evolution@swift\.org &lt;mailto:swift\-evolution@swift\.org&gt;\)
>> &gt; &gt; https://lists.swift.org/mailman/listinfo/swift-evolution
>> &gt; 
>> &gt; 
>> &gt; 
>> \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_
>> swift\-evolution mailing list
>> swift\-evolution@swift\.org &lt;mailto:swift\-evolution@swift\.org&gt;
>> https://lists.swift.org/mailman/listinfo/swift-evolution

</details>

(Haravikk) #13

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.

I get where you’re coming from, but how would people react if optional chaining wasn’t in the language yet and someone proposed it now? I know it’s not strictly the same thing, but it’s still a single question mark that prevents the whole statement from being executed. I think it would be met with a lot of resistance from people saying that being more explicit with `if let` is the way to go.

True, but with optional chaining the position of the question mark makes it a lot more clear where it stops, whereas in this proposal the question mark seems a bit less intuitive since it's within the parenthesis yet affecting the statement outside of it.

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?

The alternatives you’ve come up with would only work if foo.bar doesn’t return anything. If it does return something, and you want to assign it to a variable, you have to declare the variable beforehand and it just becomes ugly. It’s then probably a better idea to use map/flatmap:

Hmm, what about something involving the where keyword? Something like:

let value = foo.bar(x: x) where x?

Some people have queried the ability to use where in assignments before, as another way to have a statement be nil if a condition isn't met, but in this case the condition is that x is unwrapped (thus valid for the call). This basically lets you use it like a "retroactive" conditional, it'd be nice to get the same x? behaviour on for loops anyway (letting you unwrap values that way, and maybe test them too).

···

On 15 Aug 2016, at 13:44, Tim Vermeulen <tvermeulen@me.com> wrote:

On 15 Aug 2016, at 08:02, Justin Jia via swift-evolution<swift-evolution@swift.org(mailto:swift-evolution@swift.org)>wrote:


(Thorsten Seitz) #14

It is even easier if you define the methods on Cell to take optional arguments.
Then you can write the code like in your last example and don't even need the proposed syntax:

class Cell {
    let label = UILabel()
    let sublabel = UILabel()
    var heading: String?
    func humanize(_ string: String?) -> String {...} // optional argument
    func addBackgroundImage(_ image: UIImage?) // optional argument
}

extension UIImage {
    init?(named imageName: String?) {...}
}

extension String {
    init?(named imageName: Character?) {...}
}

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

-Thorsten

···

Am 15.08.2016 um 19:05 schrieb Justin Jia via swift-evolution <swift-evolution@swift.org>:

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.


(Jean-Daniel) #15

I think the `?` will make the return an optional. So your example should add another ? at the end.

print(foo(bar(42)?, baz(42)?)?)

This reads terribly, IMHO.

I agree that this reads terribly. But this is only an edge case. I would prefer let output = foo(bar(42)?, baz(42)?); print(output?). However, I want to argue that for most cases “?” reads better than “if let”.


The above example should be equivalent to:

let x = bar(42) // X is optional
let y = baz(42) // Y is optional

let z = foo(x?, y?) // Z should be optional now
print(z?)


which should be equivalent to;

let x = bar(42)
let y = baz(42)

if let x = x, let y = y {
z = foo(x, y)
}

What if baz doesn’t return optional?

If baz doesn’t return optional, then we don’t even need this syntax:

We can just write: z(bar(42)?, baz(42) and treat baz(42) like normal.

Which should be equivalent to:

let x = bar(42)
let y = baz(42)

if let x = x {
z = foo(x, y)
}


It is not consistent which what I expect when calling foo?.bar(baz(42))

In that case, if foo is nil, bad is never evaluated.

···

Le 15 août 2016 à 10:01, Justin Jia via swift-evolution <swift-evolution@swift.org> a écrit :

On Aug 15, 2016, at 3:53 PM, Charlie Monroe <charlie@charliemonroe.net <mailto:charlie@charliemonroe.net>> wrote:

On Aug 15, 2016, at 9:46 AM, Justin Jia via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

if let x = bar(42) {
  let y = baz(42)
  z = foo(x, y)
}

or

let y = baz(42)
if let x = bar(42) {
  z = foo(x, y)
}

If both are evaluated, this is really inconsistent with

if let x = bar(42), y = baz(42) { ... }

which will short-circuit and baz will not be evaluated if x is evaluated as nil.

if let z = z {
    print(z)
}
```

We don’t need to worry about “short-circuit” now because it should be equivalent to the above syntax.

> It has been mentioned before (more than once, perhaps, but not in its own thread I don't think, so good luck finding it). IIRC, one of the problems is that it's unclear what happens if your function takes multiple arguments. Does evaluation proceed from left to right? does it short-circuit? Put concretely:
>
> ```
> func bar(_ x: Int) ->Int? { /* side effects */ }
> func baz(_ y: Int) ->Int? { /* side effects */ }
> func foo(_ z: Int, _ a: Int) ->Int { /* ... */ }
>
> print(foo(bar(42)?, baz(42)?))
> ```
>
> Does baz(42) get evaluated if bar returns nil? Does bar(42) get evaluated if baz returns nil?
>
>
> On Mon, Aug 15, 2016 at 2:02 AM, Justin Jia via swift-evolution<swift-evolution@swift.org <mailto:swift-evolution@swift.org>(mailto:swift-evolution@swift.org <mailto:swift-evolution@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?
> >
> > Thanks,
> > Justin
> >
> > _______________________________________________
> > swift-evolution mailing list
> > swift-evolution@swift.org <mailto:swift-evolution@swift.org>(mailto:swift-evolution@swift.org <mailto:swift-evolution@swift.org>)
> > https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Xiaodi Wu) #16

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.

I get where you’re coming from, but how would people react if optional
chaining wasn’t in the language yet and someone proposed it now? I know
it’s not strictly the same thing, but it’s still a single question mark
that prevents the whole statement from being executed. I think it would be
met with a lot of resistance from people saying that being more explicit
with `if let` is the way to go.

True, but with optional chaining the position of the question mark makes
it a lot more clear where it stops, whereas in this proposal the question
mark seems a bit less intuitive since it's within the parenthesis yet
affecting the statement outside of it.

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?

The alternatives you’ve come up with would only work if foo.bar doesn’t
return anything. If it does return something, and you want to assign it to
a variable, you have to declare the variable beforehand and it just becomes
ugly. It’s then probably a better idea to use map/flatmap:

Hmm, what about something involving the where keyword? Something like:

let value = foo.bar(x: x) where x?

Some people have queried the ability to use where in assignments before,
as another way to have a statement be nil if a condition isn't met, but in
this case the condition is that x is unwrapped (thus valid for the call).
This basically lets you use it like a "retroactive" conditional, it'd be
nice to get the same x? behaviour on for loops anyway (letting you unwrap
values that way, and maybe test them too).

`let value = (x == nil) ? nil : foo.bar(x: x)` isn't so bad, is it? You
could even write a custom operator to sugar it.

···

On Mon, Aug 15, 2016 at 1:57 PM, Haravikk via swift-evolution < swift-evolution@swift.org> wrote:

On 15 Aug 2016, at 13:44, Tim Vermeulen <tvermeulen@me.com> wrote:
On 15 Aug 2016, at 08:02, Justin Jia via swift-evolution<swift- > evolution@swift.org(mailto:swift-evolution@swift.org > <swift-evolution@swift.org>)>wrote:

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Charles Srstka) #17

It’s distasteful, due to the need to use the force-unwrap operator. In cases like this, I usually end up writing:

let value: Foo? = nil

if let x = x {
  value = foo.bar(x: x)
} else {
  value = nil
}

or:

let value: Foo? = {
  if let x = x {
    return foo.bar(x: x)
  } else {
    return nil
  }
}()

Both of which are unwieldy, but necessary to avoid the use of !.

I wouldn’t mind something like an overload on the ternary operator:

let value = x? ? foo.bar(x: x) : nil

in which a ? after the ternary condition indicates that it is an optional to be unwrapped for the positive condition.

Charles

···

On Aug 15, 2016, at 2:27 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

`let value = (x == nil) ? nil : foo.bar(x: x)` isn't so bad, is it? You could even write a custom operator to sugar it.


(Charles Srstka) #18

This line should have been just 'let value: Foo?' .

(+1 on the proposal to move to a web-based forum that allows editing, if that’s still under consideration.)

Charles

···

On Aug 15, 2016, at 3:05 PM, Charles Srstka <cocoadev@charlessoft.com> wrote:

let value: Foo? = nil


(Xiaodi Wu) #19

`let value = (x == nil) ? nil : foo.bar(x: x)` isn't so bad, is it? You
could even write a custom operator to sugar it.

It’s distasteful, due to the need to use the force-unwrap operator. In
cases like this, I usually end up writing:

let value: Foo? = nil

if let x = x {
value = foo.bar(x: x)
} else {
value = nil
}

or:

let value: Foo? = {
if let x = x {
return foo.bar(x: x)
} else {
return nil
}
}()

Both of which are unwieldy, but necessary to avoid the use of !.

You are arguing that the force unwrap operator ! is, per se, distasteful?

···

On Mon, Aug 15, 2016 at 3:05 PM, Charles Srstka <cocoadev@charlessoft.com> wrote:

On Aug 15, 2016, at 2:27 PM, Xiaodi Wu via swift-evolution < > swift-evolution@swift.org> wrote:

I wouldn’t mind something like an overload on the ternary operator:

let value = x? ? foo.bar(x: x) : nil

in which a ? after the ternary condition indicates that it is an optional
to be unwrapped for the positive condition.

Charles


(Justin Jia) #20

Thanks for pointing this out! But I think `foo?.bar(baz(42))` should be equivalent to:

if let foo = foo {
    let x = baz(42)
    foo.bar(x)
}

IMHO, this is consistent with my example. Instead of worrying about "short-circuit", we can just treat ? as the syntax sugar of if let?

···

On Aug 15, 2016, at 4:07 PM, Jean-Daniel Dupas <mailing@xenonium.com> wrote:

Le 15 août 2016 à 10:01, Justin Jia via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> a écrit :

On Aug 15, 2016, at 3:53 PM, Charlie Monroe <charlie@charliemonroe.net <mailto:charlie@charliemonroe.net>> wrote:

On Aug 15, 2016, at 9:46 AM, Justin Jia via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I think the `?` will make the return an optional. So your example should add another ? at the end.

print(foo(bar(42)?, baz(42)?)?)

This reads terribly, IMHO.

I agree that this reads terribly. But this is only an edge case. I would prefer let output = foo(bar(42)?, baz(42)?); print(output?). However, I want to argue that for most cases “?” reads better than “if let”.


The above example should be equivalent to:

let x = bar(42) // X is optional
let y = baz(42) // Y is optional

let z = foo(x?, y?) // Z should be optional now
print(z?)


which should be equivalent to;

let x = bar(42)
let y = baz(42)

if let x = x, let y = y {
z = foo(x, y)
}

What if baz doesn’t return optional?

If baz doesn’t return optional, then we don’t even need this syntax:

We can just write: z(bar(42)?, baz(42) and treat baz(42) like normal.

Which should be equivalent to:

let x = bar(42)
let y = baz(42)

if let x = x {
z = foo(x, y)
}


It is not consistent which what I expect when calling foo?.bar(baz(42))

In that case, if foo is nil, bad is never evaluated.

if let x = bar(42) {
  let y = baz(42)
  z = foo(x, y)
}

or

let y = baz(42)
if let x = bar(42) {
  z = foo(x, y)
}

If both are evaluated, this is really inconsistent with

if let x = bar(42), y = baz(42) { ... }

which will short-circuit and baz will not be evaluated if x is evaluated as nil.

if let z = z {
    print(z)
}
```

We don’t need to worry about “short-circuit” now because it should be equivalent to the above syntax.

> It has been mentioned before (more than once, perhaps, but not in its own thread I don't think, so good luck finding it). IIRC, one of the problems is that it's unclear what happens if your function takes multiple arguments. Does evaluation proceed from left to right? does it short-circuit? Put concretely:
>
> ```
> func bar(_ x: Int) ->Int? { /* side effects */ }
> func baz(_ y: Int) ->Int? { /* side effects */ }
> func foo(_ z: Int, _ a: Int) ->Int { /* ... */ }
>
> print(foo(bar(42)?, baz(42)?))
> ```
>
> Does baz(42) get evaluated if bar returns nil? Does bar(42) get evaluated if baz returns nil?
>
>
> On Mon, Aug 15, 2016 at 2:02 AM, Justin Jia via swift-evolution<swift-evolution@swift.org <mailto:swift-evolution@swift.org>(mailto:swift-evolution@swift.org <mailto:swift-evolution@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?
> >
> > Thanks,
> > Justin
> >
> > _______________________________________________
> > swift-evolution mailing list
> > swift-evolution@swift.org <mailto:swift-evolution@swift.org>(mailto:swift-evolution@swift.org <mailto:swift-evolution@swift.org>)
> > https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution