[Idea] Use optionals for non-optional parameters

If there are two ways to do something, and one uses the force-unwrap operator while the other does not, I’ll usually opt for the latter.

Charles

···

On Aug 15, 2016, at 3:08 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

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

On Aug 15, 2016, at 2:27 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto: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.

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?

I see your point. I understand that maybe 1/2 of the people think we should evaluate both arguments and 1/2 of the people think we should only evaluate the first argument.

I changed my idea a little bit. Now I think you are right. We should only evaluate the first argument in your example. It’s not only because of inconsistent, but also because the language should at least provide a way to “short-circuit” to rest of the arguments.

If they want to opt-out this behavior, they can always write:

let x = bar(42)
let y = baz(42)
foo(x?, y?)
···

On Aug 15, 2016, at 4:09 PM, Charlie Monroe <charlie@charliemonroe.net> wrote:

The example above was to better demonstrate the problem with *when* to evaluate the latter argument. Why should both arguments be evaluated *before* the if statement? If both calls return Optionals,

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

is how would I write it without the suggested syntax - baz(42) will *not* be evaluated if bar(42) returns nil. Which bears a question why would

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

evaluate both arguments even if the first one is nil, making it incosistent with the rest of the language?

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?

In my opinion, it is. And you’d even need to write `foo.bar(x: x!)`, right?

···

On 15 Aug 2016, at 21:27, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Mon, Aug 15, 2016 at 1:57 PM, Haravikk via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On 15 Aug 2016, at 13:44, Tim Vermeulen <tvermeulen@me.com <mailto: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>(mailto:swift-evolution@swift.org <mailto:swift-evolution@swift.org>)>wrote:

You could even write a custom operator to sugar it.

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

To what benefit?

···

On Mon, Aug 15, 2016 at 16:23 Charles Srstka <cocoadev@charlessoft.com> wrote:

On Aug 15, 2016, at 3:08 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

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:

`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?

If there are two ways to do something, and one uses the force-unwrap
operator while the other does not, I’ll usually opt for the latter.

Charles

`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?

I wouldn't want ! all over my codebase, and I think the Swift language generally encourages that behaviour. In my opinion the use of ! is always caused by either a sloppy design or a language/compiler limitation. Directly comparing to nil is also something I try to avoid.

Not everyone feels this strongly about force unwrapping as I do, probably. But to me, force unwrapping is not a worthy alternative to the proposed new sugar, and I’d rather write some boilerplate code.

···

On 15 Aug 2016, at 22:08, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Mon, Aug 15, 2016 at 3:05 PM, Charles Srstka <cocoadev@charlessoft.com <mailto:cocoadev@charlessoft.com>> wrote:

On Aug 15, 2016, at 2:27 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto: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

I prefer the much simpler and natural:

let value = x.map { foo.bar(x: $0) }

Is it me or has everybody been avoiding the map solution for some reason. It's the most elegant way to do such operations IMHO.

···

On 15 Aug 2016, at 22:05, Charles Srstka via swift-evolution <swift-evolution@swift.org> wrote:

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.

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

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

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.

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

Quoting another email:

···

On Sep 4, 2016, at 12:19 AM, Thorsten Seitz <tseitz42@icloud.com> wrote:
Am 15.08.2016 um 19:05 schrieb Justin Jia via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

Actually there is an easy fix: make all functions accept optionals. I think this is a really bad idea because sometimes functions are designed to accept non-optionals.

e.g.

func addSubview(_ view: UIView) { }

It doesn’t make sense if we want to add nil as the subview, so we choose to write code like this:

if let view = view {
    addSubview(view)
}

The example above was to better demonstrate the problem with *when* to
evaluate the latter argument. Why should both arguments be evaluated
*before* the if statement? If both calls return Optionals,

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

is how would I write it without the suggested syntax - baz(42) will *not*
be evaluated if bar(42) returns nil. Which bears a question why would

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

evaluate both arguments even if the first one is nil, making it
incosistent with the rest of the language?

I see your point. I understand that maybe 1/2 of the people think we
should evaluate both arguments and 1/2 of the people think we should only
evaluate the first argument.

I changed my idea a little bit. Now I think you are right. We should only
evaluate the first argument in your example. It’s not only because of
inconsistent, but also because the language should at least provide a way
to “short-circuit” to rest of the arguments.

If they want to opt-out this behavior, they can always write:

let x = bar(42)
let y = baz(42)
foo(x?, y?)

Well, that was just the easy part. Now, suppose bar is the function that
isn't optional.

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

Is bar evaluated if baz returns nil? If you want this syntax to be sugar
for if let, then the answer is yes. If short-circuiting works
left-to-right, then the answer is no. This is very confusing, and there is
no good intuitive answer.

···

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

On Aug 15, 2016, at 4:09 PM, Charlie Monroe <charlie@charliemonroe.net> > wrote:

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

The example above was to better demonstrate the problem with *when* to
evaluate the latter argument. Why should both arguments be evaluated
*before* the if statement? If both calls return Optionals,

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

is how would I write it without the suggested syntax - baz(42) will *not*
be evaluated if bar(42) returns nil. Which bears a question why would

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

evaluate both arguments even if the first one is nil, making it
incosistent with the rest of the language?

I see your point. I understand that maybe 1/2 of the people think we
should evaluate both arguments and 1/2 of the people think we should only
evaluate the first argument.

I changed my idea a little bit. Now I think you are right. We should only
evaluate the first argument in your example. It’s not only because of
inconsistent, but also because the language should at least provide a way
to “short-circuit” to rest of the arguments.

If they want to opt-out this behavior, they can always write:

let x = bar(42)
let y = baz(42)
foo(x?, y?)

Well, that was just the easy part. Now, suppose bar is the function that
isn't optional.

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

Is bar evaluated if baz returns nil? If you want this syntax to be sugar
for if let, then the answer is yes.

s/yes/no/

If short-circuiting works left-to-right, then the answer is no.

s/no/yes/

(See? Confusing.)

···

On Mon, Aug 15, 2016 at 3:55 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

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

On Aug 15, 2016, at 4:09 PM, Charlie Monroe <charlie@charliemonroe.net> >> wrote:

This is very confusing, and there is no good intuitive answer.

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

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?

In my opinion, it is.

It certainly isn't much longer, and its meaning is clear to anyone with a
basic grasp of Swift. What makes it bad, in your opinion?

And you’d even need to write `foo.bar(x: x!)`, right?

You would. That's my bad.

You could even write a custom operator to sugar it.

infix operator ??? : NilCoalescingPrecedence
func ??? <T>(lhs: [Any?], rhs: @autoclosure () -> T) -> T? {
    for l in lhs {
        if l == nil { return nil }
    }
    return rhs()
}

var x: Int?
var y: Int?

(x, y) = (nil, nil)

[x, y] ??? x! * y!
// nil

(x, y) = (1, 2)

[x, y] ??? x! * y!
// Optional(2)
···

On Mon, Aug 15, 2016 at 2:29 PM, Tim Vermeulen <tvermeulen@me.com> wrote:

On 15 Aug 2016, at 21:27, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
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-evolutio >> n@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

IMO I don't this bar should be evaluated unless we decide if let can accept non-optional values.

Actually, what if we allow if let to accept non-optional values?

I agree this is confusing at the beginning. But people who are not familiar with the detail design can avoid this situation easily. People who are familiar with the design can adopt it quickly. Sometimes, this is unavoidable.

Btw, do you think this is still something nice to have if we limit this syntax to only variables?

···

On Aug 15, 2016, at 4:59 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Mon, Aug 15, 2016 at 3:55 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

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

On Aug 15, 2016, at 4:09 PM, Charlie Monroe <charlie@charliemonroe.net> wrote:

The example above was to better demonstrate the problem with *when* to evaluate the latter argument. Why should both arguments be evaluated *before* the if statement? If both calls return Optionals,

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

is how would I write it without the suggested syntax - baz(42) will *not* be evaluated if bar(42) returns nil. Which bears a question why would

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

evaluate both arguments even if the first one is nil, making it incosistent with the rest of the language?

I see your point. I understand that maybe 1/2 of the people think we should evaluate both arguments and 1/2 of the people think we should only evaluate the first argument.

I changed my idea a little bit. Now I think you are right. We should only evaluate the first argument in your example. It’s not only because of inconsistent, but also because the language should at least provide a way to “short-circuit” to rest of the arguments.

If they want to opt-out this behavior, they can always write:

let x = bar(42)
let y = baz(42)
foo(x?, y?)

Well, that was just the easy part. Now, suppose bar is the function that isn't optional.

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

Is bar evaluated if baz returns nil? If you want this syntax to be sugar for if let, then the answer is yes.

s/yes/no/

If short-circuiting works left-to-right, then the answer is no.

s/no/yes/

(See? Confusing.)

This is very confusing, and there is no good intuitive answer.

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

It doesn't help for functions with multiple parameters. At one point I'd made a "multimap" top-level function for iterating over multiple sequences... maybe something like that?

- Dave Sweeris

···

On Aug 15, 2016, at 17:51, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

I prefer the much simpler and natural:

let value = x.map { foo.bar(x: $0) }

Is it me or has everybody been avoiding the map solution for some reason. It's the most elegant way to do such operations IMHO.

Forgot about the ternary conditional!
For the force unwrapping, that's something that could be avoided in future by type narrowing; just as we currently can't do:

  if value is String { value.somethingUniqueToString() } // Must cast first or use if let value = value as? String

Doing the same for optionality would allow force unwrapping to be removed in future; I find having to use force unwrapping when I know I don't need to annoying, but it's hardly much of an inconvenience to be sure of what's going on.

···

On 15 Aug 2016, at 20:39, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

On Mon, Aug 15, 2016 at 2:29 PM, Tim Vermeulen <tvermeulen@me.com <mailto:tvermeulen@me.com>> wrote:

On 15 Aug 2016, at 21:27, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

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

On 15 Aug 2016, at 13:44, Tim Vermeulen <tvermeulen@me.com <mailto: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>(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?

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?

In my opinion, it is.

It certainly isn't much longer, and its meaning is clear to anyone with a basic grasp of Swift. What makes it bad, in your opinion?

And you’d even need to write `foo.bar(x: x!)`, right?

You would. That's my bad.

I agree. The proposal effectively does just that, though: turn every function into a function accepting optionals (returning nil if any argument is nil). I would prefer to do this explicitly in cases where it makes sense and use map or let-bindings elsewhere.

-Thorsten

···

Am 03.09.2016 um 18:45 schrieb Justin Jia <justin.jia.developer@gmail.com>:

On Sep 4, 2016, at 12:19 AM, Thorsten Seitz <tseitz42@icloud.com> wrote:

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.

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

Quoting another email:

Actually there is an easy fix: make all functions accept optionals. I think this is a really bad idea because sometimes functions are designed to accept non-optionals.

e.g.

func addSubview(_ view: UIView) { }

It doesn’t make sense if we want to add nil as the subview, so we choose to write code like this:

if let view = view {
    addSubview(view)
}

I was thinking about a precondition syntax. But I think it's too complicated. I agree. Maybe do this explicitly is better.

Justin

···

On Sep 4, 2016, at 1:35 AM, Thorsten Seitz <tseitz42@icloud.com> wrote:

Am 03.09.2016 um 18:45 schrieb Justin Jia <justin.jia.developer@gmail.com>:

On Sep 4, 2016, at 12:19 AM, Thorsten Seitz <tseitz42@icloud.com> wrote:

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.

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

Quoting another email:

Actually there is an easy fix: make all functions accept optionals. I think this is a really bad idea because sometimes functions are designed to accept non-optionals.

e.g.

func addSubview(_ view: UIView) { }

It doesn’t make sense if we want to add nil as the subview, so we choose to write code like this:

if let view = view {
    addSubview(view)
}

I agree. The proposal effectively does just that, though: turn every function into a function accepting optionals (returning nil if any argument is nil). I would prefer to do this explicitly in cases where it makes sense and use map or let-bindings elsewhere.

-Thorsten

What do you mean, limited to variables? What about a computed property? You
will have the same problem.

I'm not sure where you want to go with this, given that the core team has
considered the same idea in the past and found these issues to have no good
solution.

···

On Mon, Aug 15, 2016 at 04:56 Justin Jia <justin.jia.developer@gmail.com> wrote:

IMO I don't this bar should be evaluated unless we decide if let can
accept non-optional values.

Actually, what if we allow if let to accept non-optional values?

I agree this is confusing at the beginning. But people who are not
familiar with the detail design can avoid this situation easily. People who
are familiar with the design can adopt it quickly. Sometimes, this is
unavoidable.

Btw, do you think this is still something nice to have if we limit this
syntax to only variables?

On Aug 15, 2016, at 4:59 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Mon, Aug 15, 2016 at 3:55 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

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

On Aug 15, 2016, at 4:09 PM, Charlie Monroe <charlie@charliemonroe.net> >>> wrote:

The example above was to better demonstrate the problem with *when* to
evaluate the latter argument. Why should both arguments be evaluated
*before* the if statement? If both calls return Optionals,

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

is how would I write it without the suggested syntax - baz(42) will
*not* be evaluated if bar(42) returns nil. Which bears a question why would

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

evaluate both arguments even if the first one is nil, making it
incosistent with the rest of the language?

I see your point. I understand that maybe 1/2 of the people think we
should evaluate both arguments and 1/2 of the people think we should only
evaluate the first argument.

I changed my idea a little bit. Now I think you are right. We should
only evaluate the first argument in your example. It’s not only because of
inconsistent, but also because the language should at least provide a way
to “short-circuit” to rest of the arguments.

If they want to opt-out this behavior, they can always write:

let x = bar(42)
let y = baz(42)
foo(x?, y?)

Well, that was just the easy part. Now, suppose bar is the function that
isn't optional.

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

Is bar evaluated if baz returns nil? If you want this syntax to be sugar
for if let, then the answer is yes.

s/yes/no/

If short-circuiting works left-to-right, then the answer is no.

s/no/yes/

(See? Confusing.)

This is very confusing, and there is no good intuitive answer.

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

I believe the core team has considered 99% of the ideas in the mailing list in the past, but it doesn’t mean we can’t discuss it, right?

Assuming we have the following declaration:

func foo(a: Int, b: Int?, c: Int, d: Int?) -> Int

For this:

let z = foo(a: f1(), b: f2()?, c: f3(), d: f4()?) // z becomes optional

We have a few different “possible solutions”:

1. Short-circuiting from left to right. This is equivalent to:

var z: Int? = nil
let a = f1()
guard let b = f2() else { return }
let c = f3()
guard let d = f4() else { return }
z = foo(a: a, b: b, c: c, d: d)

2. Short-circuiting from left to right for optionals. Then evaluate non-optional parameters. This is equivalent to:

var z: Int? = nil
guard let b = f2() else { return }
guard let d = f4() else { return }
let a = f1()
let c = f3()
z = foo(a: a, b: b, c: c, d: d)

3. Do not short-circuiting.

var z: Int? = nil
let a = f1()
let optionalB = f2()
let c = f3()
let optionalD = f4()
guard let b = optionalB else { return }
guard let d = optionalD else { return }
z = foo(a: a, b: b, c: c, d: d)

Like I said before, I agree that there is no intuitive solution to this problem. However, I'm still not convinced that this feature is *not important*.

Thank you for pointing out the problem to me. I didn't notice it at the time I wrote my first email. I really appreciate that. However, instead of saying I don't know which is the best solution so let's assume the core team made the right decision, we should discuss whether 1, 2, 3 is the best solution. Or you can convince me we don't *need* this feature.

Back to the original topic.

I spent some time thinking and changed my mind again. I think solution 1 is most reasonable. It is consistent with if statements. Instead of treating it as sugar for `if let`, we can treat it as sugar for `guard`, which is much easy to understand and remember.

···

-

Below is the reason why I think this feature is important (quoted from another email).

The problem with `if let` is you need to call the function inside { }.

/* code 1 */
if let x = x, let y = y {
    /* code 2, depends on x and y to be non-optional */
    let z = foo(x, y)
    if let z = z {
        bar(z)
    }
    /* code 3, depends on x and y to be non-optional */
}
/* code 4 */

I can't use `guard` for this situation because guard will force me to leave the entire function.

/* code 1 */
guard let x = x, y = y else { return }
/* code 2, depends on x and y to be non-optional */
guard let z = foo(x, y) else { return }
bar(z)
/* code 3, depends on x and y to be non-optional */ <- This won't execute if z is nil
/* code 4 */ <- This won't execute if x, y or z is nil

What I really want is some like this:

/ * code 1 */
let z = foo(x?, y?)
/* code 2, depends on x and y to be non-optional, use x? and y? */
bar(z?)
/* code 3, depends on x and y to be non-optional, use x? and y? */
/* code 4 */

This is much easier to read. Sometimes people choose to use `guard` to avoid `{ }`, which usually lead to code could easily get wrong (like the second example).

Sincerely,
Justin

On Aug 15, 2016, at 11:41 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

What do you mean, limited to variables? What about a computed property? You will have the same problem.

I'm not sure where you want to go with this, given that the core team has considered the same idea in the past and found these issues to have no good solution.

On Mon, Aug 15, 2016 at 04:56 Justin Jia <justin.jia.developer@gmail.com <mailto:justin.jia.developer@gmail.com>> wrote:
IMO I don't this bar should be evaluated unless we decide if let can accept non-optional values.

Actually, what if we allow if let to accept non-optional values?

I agree this is confusing at the beginning. But people who are not familiar with the detail design can avoid this situation easily. People who are familiar with the design can adopt it quickly. Sometimes, this is unavoidable.

Btw, do you think this is still something nice to have if we limit this syntax to only variables?

On Aug 15, 2016, at 4:59 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

On Mon, Aug 15, 2016 at 3:55 AM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:
On Mon, Aug 15, 2016 at 3:25 AM, Justin Jia via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

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

The example above was to better demonstrate the problem with *when* to evaluate the latter argument. Why should both arguments be evaluated *before* the if statement? If both calls return Optionals,

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

is how would I write it without the suggested syntax - baz(42) will *not* be evaluated if bar(42) returns nil. Which bears a question why would

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

evaluate both arguments even if the first one is nil, making it incosistent with the rest of the language?

I see your point. I understand that maybe 1/2 of the people think we should evaluate both arguments and 1/2 of the people think we should only evaluate the first argument.

I changed my idea a little bit. Now I think you are right. We should only evaluate the first argument in your example. It’s not only because of inconsistent, but also because the language should at least provide a way to “short-circuit” to rest of the arguments.

If they want to opt-out this behavior, they can always write:

let x = bar(42)
let y = baz(42)
foo(x?, y?)

Well, that was just the easy part. Now, suppose bar is the function that isn't optional.

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

Is bar evaluated if baz returns nil? If you want this syntax to be sugar for if let, then the answer is yes.

s/yes/no/

If short-circuiting works left-to-right, then the answer is no.

s/no/yes/

(See? Confusing.)

This is very confusing, and there is no good intuitive answer.

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

I believe the core team has considered 99% of the ideas in the mailing
list in the past, but it doesn’t mean we can’t discuss it, right?

No, it certainly doesn't! I'm saying that you haven't come up with a
solution to a known problem with the idea.

Assuming we have the following declaration:

func foo(a: Int, b: Int?, c: Int, d: Int?) -> Int

For this:

let z = foo(a: f1(), b: f2()?, c: f3(), d: f4()?) // z becomes optional

We have a few different “possible solutions”:

1. Short-circuiting from left to right. This is equivalent to:

var z: Int? = nil
let a = f1()
guard let b = f2() else { return }
let c = f3()
guard let d = f4() else { return }
z = foo(a: a, b: b, c: c, d: d)

2. Short-circuiting from left to right for optionals. Then evaluate
non-optional parameters. This is equivalent to:

var z: Int? = nil
guard let b = f2() else { return }
guard let d = f4() else { return }
let a = f1()
let c = f3()
z = foo(a: a, b: b, c: c, d: d)

3. Do not short-circuiting.

var z: Int? = nil
let a = f1()
let optionalB = f2()
let c = f3()
let optionalD = f4()
guard let b = optionalB else { return }
guard let d = optionalD else { return }
z = foo(a: a, b: b, c: c, d: d)

Like I said before, I agree that there is no intuitive solution to this
problem. However, I'm still not convinced that this feature is *not
important*.

Thank you for pointing out the problem to me. I didn't notice it at the
time I wrote my first email. I really appreciate that. However, instead of
saying I don't know which is the best solution so let's assume the core
team made the right decision, we should discuss whether 1, 2, 3 is the best
solution. Or you can convince me we don't *need* this feature.

I'm going to convince you that 1, 2, and 3 are all bad solutions. Thus,
this feature won't fly.
The fundamental issue is that having this sugar means that I can no longer
reason about the order in which code is executed. An innocuous statement
such as `print(a(), b(), c(), d())`, once you mix in your proposed `?`
syntax with some but not all of these function calls, might have d()
executed before a(), after a(), or not at all. This is greatly damaging to
the goal of writing clear, understandable code.

Back to the original topic.

I spent some time thinking and changed my mind again. I think solution 1
is most reasonable. It is consistent with if statements. Instead of
treating it as sugar for `if let`, we can treat it as sugar for `guard`,
which is much easy to understand and remember.

-

Below is the reason why I think this feature is important (quoted from
another email).

The problem with `if let` is you need to call the function inside { }.

/* code 1 */
if let x = x, let y = y {
    /* code 2, depends on x and y to be non-optional */
    let z = foo(x, y)
    if let z = z {
        bar(z)
    }
    /* code 3, depends on x and y to be non-optional */
}
/* code 4 */

I can't use `guard` for this situation because guard will force me to
leave the entire function.

/* code 1 */
guard let x = x, y = y else { return }
/* code 2, depends on x and y to be non-optional */
guard let z = foo(x, y) else { return }
bar(z)
/* code 3, depends on x and y to be non-optional */ <- This won't execute
if z is nil
/* code 4 */ <- This won't execute if x, y or z is nil

Then surround it with a do block.

out: do {
  guard foo else { break out }
  guard bar else { break out }
  /* other code */
}
···

On Mon, Aug 15, 2016 at 11:43 AM, Justin Jia <justin.jia.developer@gmail.com > wrote:

What I really want is some like this:

/ * code 1 */
let z = foo(x?, y?)
/* code 2, depends on x and y to be non-optional, use x? and y? */
bar(z?)
/* code 3, depends on x and y to be non-optional, use x? and y? */
/* code 4 */

This is much easier to read. Sometimes people choose to use `guard` to
avoid `{ }`, which usually lead to code could easily get wrong (like the
second example).

Sincerely,
Justin

On Aug 15, 2016, at 11:41 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

What do you mean, limited to variables? What about a computed property?
You will have the same problem.

I'm not sure where you want to go with this, given that the core team has
considered the same idea in the past and found these issues to have no good
solution.

On Mon, Aug 15, 2016 at 04:56 Justin Jia <justin.jia.developer@gmail.com> > wrote:

IMO I don't this bar should be evaluated unless we decide if let can
accept non-optional values.

Actually, what if we allow if let to accept non-optional values?

I agree this is confusing at the beginning. But people who are not
familiar with the detail design can avoid this situation easily. People who
are familiar with the design can adopt it quickly. Sometimes, this is
unavoidable.

Btw, do you think this is still something nice to have if we limit this
syntax to only variables?

On Aug 15, 2016, at 4:59 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Mon, Aug 15, 2016 at 3:55 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

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

On Aug 15, 2016, at 4:09 PM, Charlie Monroe <charlie@charliemonroe.net> >>>> wrote:

The example above was to better demonstrate the problem with *when* to
evaluate the latter argument. Why should both arguments be evaluated
*before* the if statement? If both calls return Optionals,

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

is how would I write it without the suggested syntax - baz(42) will
*not* be evaluated if bar(42) returns nil. Which bears a question why would

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

evaluate both arguments even if the first one is nil, making it
incosistent with the rest of the language?

I see your point. I understand that maybe 1/2 of the people think we
should evaluate both arguments and 1/2 of the people think we should only
evaluate the first argument.

I changed my idea a little bit. Now I think you are right. We should
only evaluate the first argument in your example. It’s not only because of
inconsistent, but also because the language should at least provide a way
to “short-circuit” to rest of the arguments.

If they want to opt-out this behavior, they can always write:

let x = bar(42)
let y = baz(42)
foo(x?, y?)

Well, that was just the easy part. Now, suppose bar is the function that
isn't optional.

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

Is bar evaluated if baz returns nil? If you want this syntax to be sugar
for if let, then the answer is yes.

s/yes/no/

If short-circuiting works left-to-right, then the answer is no.

s/no/yes/

(See? Confusing.)

This is very confusing, and there is no good intuitive answer.

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

I believe the core team has considered 99% of the ideas in the mailing
list in the past, but it doesn’t mean we can’t discuss it, right?

No, it certainly doesn't! I'm saying that you haven't come up with a
solution to a known problem with the idea.

Assuming we have the following declaration:

func foo(a: Int, b: Int?, c: Int, d: Int?) -> Int

For this:

let z = foo(a: f1(), b: f2()?, c: f3(), d: f4()?) // z becomes optional

We have a few different “possible solutions”:

1. Short-circuiting from left to right. This is equivalent to:

var z: Int? = nil
let a = f1()
guard let b = f2() else { return }
let c = f3()
guard let d = f4() else { return }
z = foo(a: a, b: b, c: c, d: d)

2. Short-circuiting from left to right for optionals. Then evaluate
non-optional parameters. This is equivalent to:

var z: Int? = nil
guard let b = f2() else { return }
guard let d = f4() else { return }
let a = f1()
let c = f3()
z = foo(a: a, b: b, c: c, d: d)

3. Do not short-circuiting.

var z: Int? = nil
let a = f1()
let optionalB = f2()
let c = f3()
let optionalD = f4()
guard let b = optionalB else { return }
guard let d = optionalD else { return }
z = foo(a: a, b: b, c: c, d: d)

Like I said before, I agree that there is no intuitive solution to this
problem. However, I'm still not convinced that this feature is *not
important*.

Thank you for pointing out the problem to me. I didn't notice it at the
time I wrote my first email. I really appreciate that. However, instead of
saying I don't know which is the best solution so let's assume the core
team made the right decision, we should discuss whether 1, 2, 3 is the best
solution. Or you can convince me we don't *need* this feature.

I'm going to convince you that 1, 2, and 3 are all bad solutions. Thus,
this feature won't fly.
The fundamental issue is that having this sugar means that I can no longer
reason about the order in which code is executed. An innocuous statement
such as `print(a(), b(), c(), d())`, once you mix in your proposed `?`
syntax with some but not all of these function calls, might have d()
executed before a(), after a(), or not at all. This is greatly damaging to
the goal of writing clear, understandable code.

Back to the original topic.

I spent some time thinking and changed my mind again. I think solution 1
is most reasonable. It is consistent with if statements. Instead of
treating it as sugar for `if let`, we can treat it as sugar for `guard`,
which is much easy to understand and remember.

-

Below is the reason why I think this feature is important (quoted from
another email).

The problem with `if let` is you need to call the function inside { }.

/* code 1 */
if let x = x, let y = y {
    /* code 2, depends on x and y to be non-optional */
    let z = foo(x, y)
    if let z = z {
        bar(z)
    }
    /* code 3, depends on x and y to be non-optional */
}
/* code 4 */

I can't use `guard` for this situation because guard will force me to
leave the entire function.

/* code 1 */
guard let x = x, y = y else { return }
/* code 2, depends on x and y to be non-optional */
guard let z = foo(x, y) else { return }
bar(z)
/* code 3, depends on x and y to be non-optional */ <- This won't execute
if z is nil
/* code 4 */ <- This won't execute if x, y or z is nil

Then surround it with a do block.

out: do {
  guard foo else { break out }
  guard bar else { break out }
  /* other code */
}

Or, more idiomatically, since your use case is that you want /* code 4 */
to be executed no matter what, while everything else depends on x and y not
being nil:

defer { /* code 4 */ }
guard let x = x, let y = y else { return }
/* code 2 */
/* code 3 */
···

On Mon, Aug 15, 2016 at 12:07 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Mon, Aug 15, 2016 at 11:43 AM, Justin Jia <justin.jia.developer@gmail. > > wrote:

What I really want is some like this:

/ * code 1 */
let z = foo(x?, y?)
/* code 2, depends on x and y to be non-optional, use x? and y? */
bar(z?)
/* code 3, depends on x and y to be non-optional, use x? and y? */
/* code 4 */

This is much easier to read. Sometimes people choose to use `guard` to
avoid `{ }`, which usually lead to code could easily get wrong (like the
second example).

Sincerely,
Justin

On Aug 15, 2016, at 11:41 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

What do you mean, limited to variables? What about a computed property?
You will have the same problem.

I'm not sure where you want to go with this, given that the core team has
considered the same idea in the past and found these issues to have no good
solution.

On Mon, Aug 15, 2016 at 04:56 Justin Jia <justin.jia.developer@gmail.com> >> wrote:

IMO I don't this bar should be evaluated unless we decide if let can
accept non-optional values.

Actually, what if we allow if let to accept non-optional values?

I agree this is confusing at the beginning. But people who are not
familiar with the detail design can avoid this situation easily. People who
are familiar with the design can adopt it quickly. Sometimes, this is
unavoidable.

Btw, do you think this is still something nice to have if we limit this
syntax to only variables?

On Aug 15, 2016, at 4:59 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Mon, Aug 15, 2016 at 3:55 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

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

On Aug 15, 2016, at 4:09 PM, Charlie Monroe <charlie@charliemonroe.net> >>>>> wrote:

The example above was to better demonstrate the problem with *when* to
evaluate the latter argument. Why should both arguments be evaluated
*before* the if statement? If both calls return Optionals,

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

is how would I write it without the suggested syntax - baz(42) will
*not* be evaluated if bar(42) returns nil. Which bears a question why would

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

evaluate both arguments even if the first one is nil, making it
incosistent with the rest of the language?

I see your point. I understand that maybe 1/2 of the people think we
should evaluate both arguments and 1/2 of the people think we should only
evaluate the first argument.

I changed my idea a little bit. Now I think you are right. We should
only evaluate the first argument in your example. It’s not only because of
inconsistent, but also because the language should at least provide a way
to “short-circuit” to rest of the arguments.

If they want to opt-out this behavior, they can always write:

let x = bar(42)
let y = baz(42)
foo(x?, y?)

Well, that was just the easy part. Now, suppose bar is the function
that isn't optional.

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

Is bar evaluated if baz returns nil? If you want this syntax to be
sugar for if let, then the answer is yes.

s/yes/no/

If short-circuiting works left-to-right, then the answer is no.

s/no/yes/

(See? Confusing.)

This is very confusing, and there is no good intuitive answer.

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

Since you mentioned do and defer:

func foo(wantsToBreak: Bool) {
    out: do {
        defer { print("Hello, world!") }
        guard wantsToBreak else { break out }
    }
    print("End of function.")
}

foo(wantsToBreak: true) // Output: Hello, world!\nEnd of function.
foo(wantsToBreak: false) // Output: Hello, world!\nEnd of function.

Do you think this is confusing? At least it confused me in the fast. However, defer is still very useful.

Even if I choose to use guard, defer and do, it will still look like the one with `if let`. Lots of blocks. The code should be straightforward without any brackets.

See this example (since it’s a lot of code I rendered a PDF).

code.pdf (28.8 KB)

···

On Aug 16, 2016, at 1:16 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Mon, Aug 15, 2016 at 12:07 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:
On Mon, Aug 15, 2016 at 11:43 AM, Justin Jia <justin.jia.developer@gmail.com <mailto:justin.jia.developer@gmail.com>> wrote:
I believe the core team has considered 99% of the ideas in the mailing list in the past, but it doesn’t mean we can’t discuss it, right?

No, it certainly doesn't! I'm saying that you haven't come up with a solution to a known problem with the idea.

Assuming we have the following declaration:

func foo(a: Int, b: Int?, c: Int, d: Int?) -> Int

For this:

let z = foo(a: f1(), b: f2()?, c: f3(), d: f4()?) // z becomes optional

We have a few different “possible solutions”:

1. Short-circuiting from left to right. This is equivalent to:

var z: Int? = nil
let a = f1()
guard let b = f2() else { return }
let c = f3()
guard let d = f4() else { return }
z = foo(a: a, b: b, c: c, d: d)

2. Short-circuiting from left to right for optionals. Then evaluate non-optional parameters. This is equivalent to:

var z: Int? = nil
guard let b = f2() else { return }
guard let d = f4() else { return }
let a = f1()
let c = f3()
z = foo(a: a, b: b, c: c, d: d)

3. Do not short-circuiting.

var z: Int? = nil
let a = f1()
let optionalB = f2()
let c = f3()
let optionalD = f4()
guard let b = optionalB else { return }
guard let d = optionalD else { return }
z = foo(a: a, b: b, c: c, d: d)

Like I said before, I agree that there is no intuitive solution to this problem. However, I'm still not convinced that this feature is *not important*.

Thank you for pointing out the problem to me. I didn't notice it at the time I wrote my first email. I really appreciate that. However, instead of saying I don't know which is the best solution so let's assume the core team made the right decision, we should discuss whether 1, 2, 3 is the best solution. Or you can convince me we don't *need* this feature.

I'm going to convince you that 1, 2, and 3 are all bad solutions. Thus, this feature won't fly.
The fundamental issue is that having this sugar means that I can no longer reason about the order in which code is executed. An innocuous statement such as `print(a(), b(), c(), d())`, once you mix in your proposed `?` syntax with some but not all of these function calls, might have d() executed before a(), after a(), or not at all. This is greatly damaging to the goal of writing clear, understandable code.

Back to the original topic.

I spent some time thinking and changed my mind again. I think solution 1 is most reasonable. It is consistent with if statements. Instead of treating it as sugar for `if let`, we can treat it as sugar for `guard`, which is much easy to understand and remember.

-

Below is the reason why I think this feature is important (quoted from another email).

The problem with `if let` is you need to call the function inside { }.

/* code 1 */
if let x = x, let y = y {
    /* code 2, depends on x and y to be non-optional */
    let z = foo(x, y)
    if let z = z {
        bar(z)
    }
    /* code 3, depends on x and y to be non-optional */
}
/* code 4 */

I can't use `guard` for this situation because guard will force me to leave the entire function.

/* code 1 */
guard let x = x, y = y else { return }
/* code 2, depends on x and y to be non-optional */
guard let z = foo(x, y) else { return }
bar(z)
/* code 3, depends on x and y to be non-optional */ <- This won't execute if z is nil
/* code 4 */ <- This won't execute if x, y or z is nil

Then surround it with a do block.

out: do {
  guard foo else { break out }
  guard bar else { break out }
  /* other code */
}

Or, more idiomatically, since your use case is that you want /* code 4 */ to be executed no matter what, while everything else depends on x and y not being nil:

defer { /* code 4 */ }
guard let x = x, let y = y else { return }
/* code 2 */
/* code 3 */

What I really want is some like this:

/ * code 1 */
let z = foo(x?, y?)
/* code 2, depends on x and y to be non-optional, use x? and y? */
bar(z?)
/* code 3, depends on x and y to be non-optional, use x? and y? */
/* code 4 */

This is much easier to read. Sometimes people choose to use `guard` to avoid `{ }`, which usually lead to code could easily get wrong (like the second example).

Sincerely,
Justin

On Aug 15, 2016, at 11:41 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

What do you mean, limited to variables? What about a computed property? You will have the same problem.

I'm not sure where you want to go with this, given that the core team has considered the same idea in the past and found these issues to have no good solution.

On Mon, Aug 15, 2016 at 04:56 Justin Jia <justin.jia.developer@gmail.com <mailto:justin.jia.developer@gmail.com>> wrote:
IMO I don't this bar should be evaluated unless we decide if let can accept non-optional values.

Actually, what if we allow if let to accept non-optional values?

I agree this is confusing at the beginning. But people who are not familiar with the detail design can avoid this situation easily. People who are familiar with the design can adopt it quickly. Sometimes, this is unavoidable.

Btw, do you think this is still something nice to have if we limit this syntax to only variables?

On Aug 15, 2016, at 4:59 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

On Mon, Aug 15, 2016 at 3:55 AM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:
On Mon, Aug 15, 2016 at 3:25 AM, Justin Jia via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

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

The example above was to better demonstrate the problem with *when* to evaluate the latter argument. Why should both arguments be evaluated *before* the if statement? If both calls return Optionals,

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

is how would I write it without the suggested syntax - baz(42) will *not* be evaluated if bar(42) returns nil. Which bears a question why would

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

evaluate both arguments even if the first one is nil, making it incosistent with the rest of the language?

I see your point. I understand that maybe 1/2 of the people think we should evaluate both arguments and 1/2 of the people think we should only evaluate the first argument.

I changed my idea a little bit. Now I think you are right. We should only evaluate the first argument in your example. It’s not only because of inconsistent, but also because the language should at least provide a way to “short-circuit” to rest of the arguments.

If they want to opt-out this behavior, they can always write:

let x = bar(42)
let y = baz(42)
foo(x?, y?)

Well, that was just the easy part. Now, suppose bar is the function that isn't optional.

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

Is bar evaluated if baz returns nil? If you want this syntax to be sugar for if let, then the answer is yes.

s/yes/no/

If short-circuiting works left-to-right, then the answer is no.

s/no/yes/

(See? Confusing.)

This is very confusing, and there is no good intuitive answer.

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