Optional Argument Chaining

Hi all,

I'd like to propose a syntax addition that acts to ease some things that I believe should fall under the umbrella of 'optional chaining'. Optional chaining allows us to access the properties of an optional value and return nil if any link in that chain breaks. I propose we introduce syntax to allow similar chaining when passing optional valued parameters to functions that expect that parameter to be non-optional.

The example below is taken from a project I'm working on at the moment:

// Current
let readme: Markdown?
if let rawMarkdown = String(contentsOfFile: "README.md") {
  readme = Markdown(string: rawMarkdown)
} else {
  readme = nil
}
In this example we want to perform an operation, the initialisation of a 'Markdown' type, with our raw text if it exists and get nil otherwise. This is rather verbose

I propose the following syntax for an alternative:

// Proposed alternative
let readme = Markdown(string: String(contentsOfFile: "README.md")?)

The ? is familiar in its use for optional chaining.

This would act like syntactic sugar for the flatMap method on Optional. For example:

(where `john` is of a `Person` type with a property `address: Address?`)
// func getZipCode(fromAddress address: Address) -> ZipCode
getZipCode(fromAddress: john.address?)

// Would be equivalent to…
john.address.flatMap {
  getZipCode($0)
}
An example with multiple parameters:

// func getPostageEstimate(source: Address, destination: Address, weight: Double) -> Int
getPostageEstimate(source: john.address?, destination: alice.address?, weight: 2.0)

// Equivalent to
john.address.flatMap { freshVar1 in
  alice.address.flatMap { freshVar2 in
    getPostageEstimate(source: freshVar1, destination: freshVar2, weight: 2.0)
  }
}

// Or equally:
{
  guard let freshVar1 = john.address,
    let freshVar2 = alice.address else {
      return nil
  }

  return getPostageEstimate(source: freshVar1, destination: freshVar2, weight: 2.0)
}()
This would only be allowed when the parameter doesn’t already accept Optionals and when the chained value is in fact an optional. We’d want to consider emitting at least the following errors/warnings in the given scenarios:

let result = myFunc(3?)
// error: cannot use optional chaining on non-optional value of type 'Int'

// func myFunc2(x: String?) -> String
let result = myFunc2(x: john.address?)
// error: cannot use optional argument chaining on argument of optional type
let result = myFunc(nil?)
// warning: optional argument chaining with nil literal always results in nil

Seeking your thoughts on this idea, the specific syntax, and more use case examples.

Best,

Jared

2 Likes

You talk about flatMap without giving an example. The readme isn't that bad with it, IMO:

if let readme = String(contentsOfFile: "README.md").flatMap(Markdown) {
  // use contents here
}

That doesn't work when you need multiple optional parameters. In my own experience, that hasn't been a huge problem, though.

Félix

···

Le 11 déc. 2017 à 08:30, Jared Khan via swift-evolution <swift-evolution@swift.org> a écrit :

Hi all,

I'd like to propose a syntax addition that acts to ease some things that I believe should fall under the umbrella of 'optional chaining'. Optional chaining allows us to access the properties of an optional value and return nil if any link in that chain breaks. I propose we introduce syntax to allow similar chaining when passing optional valued parameters to functions that expect that parameter to be non-optional.

The example below is taken from a project I'm working on at the moment:

// Current
let readme: Markdown?
if let rawMarkdown = String(contentsOfFile: "README.md") {
  readme = Markdown(string: rawMarkdown)
} else {
  readme = nil
}
In this example we want to perform an operation, the initialisation of a 'Markdown' type, with our raw text if it exists and get nil otherwise. This is rather verbose

I propose the following syntax for an alternative:

// Proposed alternative
let readme = Markdown(string: String(contentsOfFile: "README.md")?)

The ? is familiar in its use for optional chaining.

This would act like syntactic sugar for the flatMap method on Optional. For example:

(where `john` is of a `Person` type with a property `address: Address?`)
// func getZipCode(fromAddress address: Address) -> ZipCode
getZipCode(fromAddress: john.address?)

// Would be equivalent to…
john.address.flatMap {
  getZipCode($0)
}
An example with multiple parameters:

// func getPostageEstimate(source: Address, destination: Address, weight: Double) -> Int
getPostageEstimate(source: john.address?, destination: alice.address?, weight: 2.0)

// Equivalent to
john.address.flatMap { freshVar1 in
  alice.address.flatMap { freshVar2 in
    getPostageEstimate(source: freshVar1, destination: freshVar2, weight: 2.0)
  }
}

// Or equally:
{
  guard let freshVar1 = john.address,
    let freshVar2 = alice.address else {
      return nil
  }

  return getPostageEstimate(source: freshVar1, destination: freshVar2, weight: 2.0)
}()
This would only be allowed when the parameter doesn’t already accept Optionals and when the chained value is in fact an optional. We’d want to consider emitting at least the following errors/warnings in the given scenarios:

let result = myFunc(3?)
// error: cannot use optional chaining on non-optional value of type 'Int'

// func myFunc2(x: String?) -> String
let result = myFunc2(x: john.address?)
// error: cannot use optional argument chaining on argument of optional type
let result = myFunc(nil?)
// warning: optional argument chaining with nil literal always results in nil

Seeking your thoughts on this idea, the specific syntax, and more use case examples.

Best,

Jared

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

1. Am I right in assuming that you propose that the suffix operator "?" would make the result of the surrounding method/function call optional, so that a(b(c?)) would make the result of the "b" function call optional, but not the "a" function call, and that it would be a(b(c?)?) if we would like to propagate this two levels?

2. If that is the case, is that understandable/neat enough? How common would you expect this to be?

3. For some reason, (in current Swift) the suffix operator "?" seems to be allowed in intra-expression syntax, and only fails when the inter-expression syntax is checked for optionality congruence. Is there a reason for this? I would have expected that the congruence error "cannot use optional chaining on non-optional value of type" would never be seen for a lone "?", since the error message "'?' must be followed by a call, member lookup, or subscript" would always be displayed first if it was checked first. The "." operator checks intra-expression syntax first, before checking congruence. Is this a sign that "?" as a suffix operator is already somewhat operational as an operator for optional types? I have a faint recollection that it was doing something in earlier versions of Swift.

/Magnus

···

12 Dec. 2017 01:30 Jared Khan via swift-evolution <swift-evolution@swift.org> wrote:

I'd like to propose a syntax addition that acts to ease some things that I believe should fall under the umbrella of 'optional chaining'. Optional chaining allows us to access the properties of an optional value and return nil if any link in that chain breaks. I propose we introduce syntax to allow similar chaining when passing optional valued parameters to functions that expect that parameter to be non-optional.

You can create a binary operator that tests the left-hand operand for nil, and passes the unwrapped value to the right-hand operand (a function taking one value), this operator can be made left-associative to allow chaining.

let m = String(contentsOfFile: "README.md") ??? Markdown
where ??? is the operator described above.

···

--
C. Keith Ray

* What Every Programmer Needs To… by C. Keith Ray [PDF/iPad/Kindle] <- buy my book?
* http://www.thirdfoundationsw.com/keith_ray_resume_2014_long.pdf
* http://agilesolutionspace.blogspot.com/

On Dec 11, 2017, at 9:07 AM, Magnus Ahltorp via swift-evolution <swift-evolution@swift.org> wrote:

12 Dec. 2017 01:30 Jared Khan via swift-evolution <swift-evolution@swift.org> wrote:

I'd like to propose a syntax addition that acts to ease some things that I believe should fall under the umbrella of 'optional chaining'. Optional chaining allows us to access the properties of an optional value and return nil if any link in that chain breaks. I propose we introduce syntax to allow similar chaining when passing optional valued parameters to functions that expect that parameter to be non-optional.

1. Am I right in assuming that you propose that the suffix operator "?" would make the result of the surrounding method/function call optional, so that a(b(c?)) would make the result of the "b" function call optional, but not the "a" function call, and that it would be a(b(c?)?) if we would like to propagate this two levels?

2. If that is the case, is that understandable/neat enough? How common would you expect this to be?

3. For some reason, (in current Swift) the suffix operator "?" seems to be allowed in intra-expression syntax, and only fails when the inter-expression syntax is checked for optionality congruence. Is there a reason for this? I would have expected that the congruence error "cannot use optional chaining on non-optional value of type" would never be seen for a lone "?", since the error message "'?' must be followed by a call, member lookup, or subscript" would always be displayed first if it was checked first. The "." operator checks intra-expression syntax first, before checking congruence. Is this a sign that "?" as a suffix operator is already somewhat operational as an operator for optional types? I have a faint recollection that it was doing something in earlier versions of Swift.

/Magnus

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

I don’t think that solves the same thing. The problem is I read it is to allow some sort of short hand to be able to pass optional types as parameters into methods / initializers that do not take optional types. The result of that method / initializer being nil if any of said parameters were nil or a typical result otherwise. Based on your example I think it’s identical to the existing ?? operator, is it not?

As per the suggested syntax, I do think that we would want to be able to control this feature on a per parameter basis, so it would make sense to have the ? operator placed after the parameter that needed to be unwrapped. This would also be more consistent with the usage of the existing syntax.

— Charles

···

On Dec 11, 2017, at 9:37 AM, C. Keith Ray via swift-evolution <swift-evolution@swift.org> wrote:

You can create a binary operator that tests the left-hand operand for nil, and passes the unwrapped value to the right-hand operand (a function taking one value), this operator can be made left-associative to allow chaining.

let m = String(contentsOfFile: "README.md") ??? Markdown
where ??? is the operator described above.

--
C. Keith Ray

* What Every Programmer Needs To… by C. Keith Ray [PDF/iPad/Kindle] <- buy my book?
* http://www.thirdfoundationsw.com/keith_ray_resume_2014_long.pdf
* http://agilesolutionspace.blogspot.com/

On Dec 11, 2017, at 9:07 AM, Magnus Ahltorp via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

12 Dec. 2017 01:30 Jared Khan via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I'd like to propose a syntax addition that acts to ease some things that I believe should fall under the umbrella of 'optional chaining'. Optional chaining allows us to access the properties of an optional value and return nil if any link in that chain breaks. I propose we introduce syntax to allow similar chaining when passing optional valued parameters to functions that expect that parameter to be non-optional.

1. Am I right in assuming that you propose that the suffix operator "?" would make the result of the surrounding method/function call optional, so that a(b(c?)) would make the result of the "b" function call optional, but not the "a" function call, and that it would be a(b(c?)?) if we would like to propagate this two levels?

2. If that is the case, is that understandable/neat enough? How common would you expect this to be?

3. For some reason, (in current Swift) the suffix operator "?" seems to be allowed in intra-expression syntax, and only fails when the inter-expression syntax is checked for optionality congruence. Is there a reason for this? I would have expected that the congruence error "cannot use optional chaining on non-optional value of type" would never be seen for a lone "?", since the error message "'?' must be followed by a call, member lookup, or subscript" would always be displayed first if it was checked first. The "." operator checks intra-expression syntax first, before checking congruence. Is this a sign that "?" as a suffix operator is already somewhat operational as an operator for optional types? I have a faint recollection that it was doing something in earlier versions of Swift.

/Magnus

_______________________________________________
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

It’s worth mentioning that the problem this thread is discussing can be generalized to idioms / applicative. The specific case here is for Optional but many other types could benefit from an elegant syntactic solution to this problem. It might be worth exploring a more general solution. Here’s a link to how this is handled in Idris: Interfaces — Idris 1.3.3 documentation.

Matthew

···

On Dec 11, 2017, at 12:01 PM, Jared Khan via swift-evolution <swift-evolution@swift.org> wrote:

1. Correct

2. It felt natural to me. It’s analogous to the existing optional chaining scenarios and composes nicely. I think it’s about as understandable as existing chaining, a newbie would have to look it up to discover its meaning. What are your thoughts on this particular syntax (ignoring 3. momentarily)? Hopefully others in this thread can share their views too.
As for how common I’d expect it to be, it’s something I’ve run into myself a few times. Again, I hope members of this list can give their view on if this would be useful to them.

3. I’m not entirely sure what the grammar situation is yet but afaik ‘?’ has never been available as a postfix operator. Perhaps I’m missing your point, could you demonstrate where it is allowed?

Best,
Jared

On 11 Dec 2017, at 17:07, Magnus Ahltorp <map@kth.se> wrote:

12 Dec. 2017 01:30 Jared Khan via swift-evolution <swift-evolution@swift.org> wrote:

I'd like to propose a syntax addition that acts to ease some things that I believe should fall under the umbrella of 'optional chaining'. Optional chaining allows us to access the properties of an optional value and return nil if any link in that chain breaks. I propose we introduce syntax to allow similar chaining when passing optional valued parameters to functions that expect that parameter to be non-optional.

1. Am I right in assuming that you propose that the suffix operator "?" would make the result of the surrounding method/function call optional, so that a(b(c?)) would make the result of the "b" function call optional, but not the "a" function call, and that it would be a(b(c?)?) if we would like to propagate this two levels?

2. If that is the case, is that understandable/neat enough? How common would you expect this to be?

3. For some reason, (in current Swift) the suffix operator "?" seems to be allowed in intra-expression syntax, and only fails when the inter-expression syntax is checked for optionality congruence. Is there a reason for this? I would have expected that the congruence error "cannot use optional chaining on non-optional value of type" would never be seen for a lone "?", since the error message "'?' must be followed by a call, member lookup, or subscript" would always be displayed first if it was checked first. The "." operator checks intra-expression syntax first, before checking congruence. Is this a sign that "?" as a suffix operator is already somewhat operational as an operator for optional types? I have a faint recollection that it was doing something in earlier versions of Swift.

/Magnus

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

1. Correct

2. It felt natural to me. It’s analogous to the existing optional chaining scenarios and composes nicely. I think it’s about as understandable as existing chaining, a newbie would have to look it up to discover its meaning. What are your thoughts on this particular syntax (ignoring 3. momentarily)? Hopefully others in this thread can share their views too.
As for how common I’d expect it to be, it’s something I’ve run into myself a few times. Again, I hope members of this list can give their view on if this would be useful to them.

3. I’m not entirely sure what the grammar situation is yet but afaik ‘?’ has never been available as a postfix operator. Perhaps I’m missing your point, could you demonstrate where it is allowed?

Best,
Jared

···

On 11 Dec 2017, at 17:07, Magnus Ahltorp <map@kth.se> wrote:

12 Dec. 2017 01:30 Jared Khan via swift-evolution <swift-evolution@swift.org> wrote:

I'd like to propose a syntax addition that acts to ease some things that I believe should fall under the umbrella of 'optional chaining'. Optional chaining allows us to access the properties of an optional value and return nil if any link in that chain breaks. I propose we introduce syntax to allow similar chaining when passing optional valued parameters to functions that expect that parameter to be non-optional.

1. Am I right in assuming that you propose that the suffix operator "?" would make the result of the surrounding method/function call optional, so that a(b(c?)) would make the result of the "b" function call optional, but not the "a" function call, and that it would be a(b(c?)?) if we would like to propagate this two levels?

2. If that is the case, is that understandable/neat enough? How common would you expect this to be?

3. For some reason, (in current Swift) the suffix operator "?" seems to be allowed in intra-expression syntax, and only fails when the inter-expression syntax is checked for optionality congruence. Is there a reason for this? I would have expected that the congruence error "cannot use optional chaining on non-optional value of type" would never be seen for a lone "?", since the error message "'?' must be followed by a call, member lookup, or subscript" would always be displayed first if it was checked first. The "." operator checks intra-expression syntax first, before checking congruence. Is this a sign that "?" as a suffix operator is already somewhat operational as an operator for optional types? I have a faint recollection that it was doing something in earlier versions of Swift.

/Magnus

2. It felt natural to me. It’s analogous to the existing optional chaining scenarios and composes nicely. I think it’s about as understandable as existing chaining, a newbie would have to look it up to discover its meaning. What are your thoughts on this particular syntax (ignoring 3. momentarily)? Hopefully others in this thread can share their views too.

Chaining methods is linear, while nesting fills a similar purpose when we use function calls. This of course affects the way existing Swift code is written, but that is something we have to live with if we want to use familiar syntax patterns. However, I think we have to consider this difference in this case, since the syntax becomes more convoluted. Your suggestion is definitely not as easy to read as the optional chaining syntax, and maybe it can't be.

As for how common I’d expect it to be, it’s something I’ve run into myself a few times. Again, I hope members of this list can give their view on if this would be useful to them.

I don't have any real examples, but I certainly think that I have run into it, so I'm quite open to solving the problem. For me, it is probably only a matter of finding a syntax that is acceptable.

3. I’m not entirely sure what the grammar situation is yet but afaik ‘?’ has never been available as a postfix operator. Perhaps I’m missing your point, could you demonstrate where it is allowed?

I did not expect that you would be able to answer that, it was more a question directed to people who are more connected to the inner workings of the parsing of Swift than I am. It is not allowed, but the error message is not the one I expect, something that gives me a hint that it does have some meaning early in the parsing.

/Magnus

···

12 Dec. 2017 02:58 Jared Khan <jaredkhan@me.com> wrote:

Even this small example I think this is still a little fiddly. If we add another required parameter to the Markdown initializer:
  let readme = String(contentsOfFile: “README.md”).flatMap { Markdown(string: $0, flavor: .github) }
this starts to feel a little inside-out and crufty to me.

···

On 12 Dec 2017, at 05:54, Félix Cloutier <felixcloutier@icloud.com> wrote:

You talk about flatMap without giving an example. The readme isn't that bad with it, IMO:

if let readme = String(contentsOfFile: "README.md").flatMap(Markdown) {
  // use contents here
}

That doesn't work when you need multiple optional parameters. In my own experience, that hasn't been a huge problem, though.

Félix

Le 11 déc. 2017 à 08:30, Jared Khan via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> a écrit :

Hi all,

I'd like to propose a syntax addition that acts to ease some things that I believe should fall under the umbrella of 'optional chaining'. Optional chaining allows us to access the properties of an optional value and return nil if any link in that chain breaks. I propose we introduce syntax to allow similar chaining when passing optional valued parameters to functions that expect that parameter to be non-optional.

The example below is taken from a project I'm working on at the moment:

// Current
let readme: Markdown?
if let rawMarkdown = String(contentsOfFile: "README.md") {
  readme = Markdown(string: rawMarkdown)
} else {
  readme = nil
}
In this example we want to perform an operation, the initialisation of a 'Markdown' type, with our raw text if it exists and get nil otherwise. This is rather verbose

I propose the following syntax for an alternative:

// Proposed alternative
let readme = Markdown(string: String(contentsOfFile: "README.md")?)

The ? is familiar in its use for optional chaining.

This would act like syntactic sugar for the flatMap method on Optional. For example:

(where `john` is of a `Person` type with a property `address: Address?`)
// func getZipCode(fromAddress address: Address) -> ZipCode
getZipCode(fromAddress: john.address?)

// Would be equivalent to…
john.address.flatMap {
  getZipCode($0)
}
An example with multiple parameters:

// func getPostageEstimate(source: Address, destination: Address, weight: Double) -> Int
getPostageEstimate(source: john.address?, destination: alice.address?, weight: 2.0)

// Equivalent to
john.address.flatMap { freshVar1 in
  alice.address.flatMap { freshVar2 in
    getPostageEstimate(source: freshVar1, destination: freshVar2, weight: 2.0)
  }
}

// Or equally:
{
  guard let freshVar1 = john.address,
    let freshVar2 = alice.address else {
      return nil
  }

  return getPostageEstimate(source: freshVar1, destination: freshVar2, weight: 2.0)
}()
This would only be allowed when the parameter doesn’t already accept Optionals and when the chained value is in fact an optional. We’d want to consider emitting at least the following errors/warnings in the given scenarios:

let result = myFunc(3?)
// error: cannot use optional chaining on non-optional value of type 'Int'

// func myFunc2(x: String?) -> String
let result = myFunc2(x: john.address?)
// error: cannot use optional argument chaining on argument of optional type
let result = myFunc(nil?)
// warning: optional argument chaining with nil literal always results in nil

Seeking your thoughts on this idea, the specific syntax, and more use case examples.

Best,

Jared

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

Just want to +1 a more general, less "Optional"-specific solution. You can do a ton of interesting things with applicative structure.

Failures from "Optional" and "throws" operate sequentially and halt immediately: the first "nil" or "throw" encountered prevents all work chained from it[1]. Another upcoming sequential operation that's worth thinking about in this discussion: "async"/"await".

Applicative structures throw away that sequential constraint. Let's consider some fun things that happen when we take it away in a couple of the examples above.

If "throws" were applicative, you could accumulate a bunch of errors at once.

    do {
      // made-up syntax
      let user = User(|name: try validate(name: name), email: try validate(email: email)|)
    } catch {
      print(error) // .manyErrors(["name is too short", "email is invalid"])
    }

Currently, the above would halt on the first error.

If "async"/"await" were applicative, you could fire off a bunch of asynchronous requests in parallel.

    let homepageData = HomepageData(|await fetchCurrentUser(), await fetchProducts()|)

In the proposed version of "async"/"await", the above would block on "fetchCurrentUser()" and only call "fetchProducts()" after the response.

"Optional" would get to use that same sugar! An example from the original email:

    getPostageEstimate(|source: john.address, destination: alice.address, weight 2.0|)

···

On Dec 11, 2017, at 1:08 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

It’s worth mentioning that the problem this thread is discussing can be generalized to idioms / applicative. The specific case here is for Optional but many other types could benefit from an elegant syntactic solution to this problem. It might be worth exploring a more general solution. Here’s a link to how this is handled in Idris: Interfaces — Idris 1.3.3 documentation.

Matthew

--
[1]: "Optional" and "throws" are monadic structures and "flatMap" is the abstraction of this sequential operation.

Stephen

I missed the previous threads! I’ve found one of the relevant threads here:
[swift-evolution] Proposal: Extend Optional Chaining to Function, Initializer, and Subscript Parameters

Thanks for this important point.

If you were to write this logic out by hand then you would short-circuit it and this is analogous to current chaining behaviour so to me evaluating left to right (as Swift usually does) and stopping at the first failed unwrap would make sense. I wouldn’t necessarily say it’s intuitive but I don’t think it’s really less intuitive than the current chaining behaviour.

I think it gets confusing when you have multiple levels of nested expressions, eg

foo(bar(x?)) + y?

Slava

···

On Dec 11, 2017, at 2:41 PM, Jared Khan via swift-evolution <swift-evolution@swift.org> wrote:

Jared

On 11 Dec 2017, at 19:28, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

This topic has been discussed at least two and maybe more times in the past.. It’s hard for me to post links at the moment, but it should be possible to find on Google.

One major challenge to this idea, for which no satisfactory answer has been achieved after all these years, is the following issue:

f(g()?, h()?, i(), j()?)?

If g() evaluates to nil, is h() called or not? How about i(), which is not failable? Since any function or property access can have side effects, in what order are the arguments evaluated, and how does one reason about this code flow?

To my mind, in the absence of an intuitive answer to the above—which does not appear to be possible—this idea is not feasible.
On Mon, Dec 11, 2017 at 12:34 Magnus Ahltorp via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

> 12 Dec. 2017 02:58 Jared Khan <jaredkhan@me.com <mailto:jaredkhan@me.com>> wrote:
>
> 2. It felt natural to me. It’s analogous to the existing optional chaining scenarios and composes nicely. I think it’s about as understandable as existing chaining, a newbie would have to look it up to discover its meaning. What are your thoughts on this particular syntax (ignoring 3. momentarily)? Hopefully others in this thread can share their views too.

Chaining methods is linear, while nesting fills a similar purpose when we use function calls. This of course affects the way existing Swift code is written, but that is something we have to live with if we want to use familiar syntax patterns. However, I think we have to consider this difference in this case, since the syntax becomes more convoluted. Your suggestion is definitely not as easy to read as the optional chaining syntax, and maybe it can't be.

> As for how common I’d expect it to be, it’s something I’ve run into myself a few times. Again, I hope members of this list can give their view on if this would be useful to them.

I don't have any real examples, but I certainly think that I have run into it, so I'm quite open to solving the problem. For me, it is probably only a matter of finding a syntax that is acceptable.

> 3. I’m not entirely sure what the grammar situation is yet but afaik ‘?’ has never been available as a postfix operator. Perhaps I’m missing your point, could you demonstrate where it is allowed?

I did not expect that you would be able to answer that, it was more a question directed to people who are more connected to the inner workings of the parsing of Swift than I am. It is not allowed, but the error message is not the one I expect, something that gives me a hint that it does have some meaning early in the parsing.

/Magnus

_______________________________________________
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

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

i’ve run into this problem enough to wish this was a thing in Swift a lot,
but when it comes to solutions, i think the proposed syntax just isn’t
clear or easy to read, especially if there’s multiple layers involved.

···

On Mon, Dec 11, 2017 at 1:28 PM, Xiaodi Wu via swift-evolution < swift-evolution@swift.org> wrote:

This topic has been discussed at least two and maybe more times in the
past. It’s hard for me to post links at the moment, but it should be
possible to find on Google.

One major challenge to this idea, for which no satisfactory answer has
been achieved after all these years, is the following issue:

f(g()?, h()?, i(), j()?)?

If g() evaluates to nil, is h() called or not? How about i(), which is not
failable? Since any function or property access can have side effects, in
what order are the arguments evaluated, and how does one reason about this
code flow?

To my mind, in the absence of an intuitive answer to the above—which does
not appear to be possible—this idea is not feasible.

On Mon, Dec 11, 2017 at 12:34 Magnus Ahltorp via swift-evolution < > swift-evolution@swift.org> wrote:

> 12 Dec. 2017 02:58 Jared Khan <jaredkhan@me.com> wrote:
>
> 2. It felt natural to me. It’s analogous to the existing optional
chaining scenarios and composes nicely. I think it’s about as
understandable as existing chaining, a newbie would have to look it up to
discover its meaning. What are your thoughts on this particular syntax
(ignoring 3. momentarily)? Hopefully others in this thread can share their
views too.

Chaining methods is linear, while nesting fills a similar purpose when we
use function calls. This of course affects the way existing Swift code is
written, but that is something we have to live with if we want to use
familiar syntax patterns. However, I think we have to consider this
difference in this case, since the syntax becomes more convoluted. Your
suggestion is definitely not as easy to read as the optional chaining
syntax, and maybe it can't be.

> As for how common I’d expect it to be, it’s something I’ve run into
myself a few times. Again, I hope members of this list can give their view
on if this would be useful to them.

I don't have any real examples, but I certainly think that I have run
into it, so I'm quite open to solving the problem. For me, it is probably
only a matter of finding a syntax that is acceptable.

> 3. I’m not entirely sure what the grammar situation is yet but afaik
‘?’ has never been available as a postfix operator. Perhaps I’m missing
your point, could you demonstrate where it is allowed?

I did not expect that you would be able to answer that, it was more a
question directed to people who are more connected to the inner workings of
the parsing of Swift than I am. It is not allowed, but the error message is
not the one I expect, something that gives me a hint that it does have some
meaning early in the parsing.

/Magnus

_______________________________________________
swift-evolution mailing list
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

This topic has been discussed at least two and maybe more times in the
past. It’s hard for me to post links at the moment, but it should be
possible to find on Google.

One major challenge to this idea, for which no satisfactory answer has been
achieved after all these years, is the following issue:

f(g()?, h()?, i(), j()?)?

If g() evaluates to nil, is h() called or not? How about i(), which is not
failable? Since any function or property access can have side effects, in
what order are the arguments evaluated, and how does one reason about this
code flow?

To my mind, in the absence of an intuitive answer to the above—which does
not appear to be possible—this idea is not feasible.

···

On Mon, Dec 11, 2017 at 12:34 Magnus Ahltorp via swift-evolution < swift-evolution@swift.org> wrote:

> 12 Dec. 2017 02:58 Jared Khan <jaredkhan@me.com> wrote:
>
> 2. It felt natural to me. It’s analogous to the existing optional
chaining scenarios and composes nicely. I think it’s about as
understandable as existing chaining, a newbie would have to look it up to
discover its meaning. What are your thoughts on this particular syntax
(ignoring 3. momentarily)? Hopefully others in this thread can share their
views too.

Chaining methods is linear, while nesting fills a similar purpose when we
use function calls. This of course affects the way existing Swift code is
written, but that is something we have to live with if we want to use
familiar syntax patterns. However, I think we have to consider this
difference in this case, since the syntax becomes more convoluted. Your
suggestion is definitely not as easy to read as the optional chaining
syntax, and maybe it can't be.

> As for how common I’d expect it to be, it’s something I’ve run into
myself a few times. Again, I hope members of this list can give their view
on if this would be useful to them.

I don't have any real examples, but I certainly think that I have run into
it, so I'm quite open to solving the problem. For me, it is probably only a
matter of finding a syntax that is acceptable.

> 3. I’m not entirely sure what the grammar situation is yet but afaik ‘?’
has never been available as a postfix operator. Perhaps I’m missing your
point, could you demonstrate where it is allowed?

I did not expect that you would be able to answer that, it was more a
question directed to people who are more connected to the inner workings of
the parsing of Swift than I am. It is not allowed, but the error message is
not the one I expect, something that gives me a hint that it does have some
meaning early in the parsing.

/Magnus

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

I missed the previous threads! I’ve found one of the relevant threads here:
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160711/024201.html

Thanks for this important point.

If you were to write this logic out by hand then you would short-circuit it and this is analogous to current chaining behaviour so to me evaluating left to right (as Swift usually does) and stopping at the first failed unwrap would make sense. I wouldn’t necessarily say it’s intuitive but I don’t think it’s really less intuitive than the current chaining behaviour.

Jared

···

On 11 Dec 2017, at 19:28, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

This topic has been discussed at least two and maybe more times in the past.. It’s hard for me to post links at the moment, but it should be possible to find on Google.

One major challenge to this idea, for which no satisfactory answer has been achieved after all these years, is the following issue:

f(g()?, h()?, i(), j()?)?

If g() evaluates to nil, is h() called or not? How about i(), which is not failable? Since any function or property access can have side effects, in what order are the arguments evaluated, and how does one reason about this code flow?

To my mind, in the absence of an intuitive answer to the above—which does not appear to be possible—this idea is not feasible.
On Mon, Dec 11, 2017 at 12:34 Magnus Ahltorp via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

> 12 Dec. 2017 02:58 Jared Khan <jaredkhan@me.com <mailto:jaredkhan@me.com>> wrote:
>
> 2. It felt natural to me. It’s analogous to the existing optional chaining scenarios and composes nicely. I think it’s about as understandable as existing chaining, a newbie would have to look it up to discover its meaning. What are your thoughts on this particular syntax (ignoring 3. momentarily)? Hopefully others in this thread can share their views too.

Chaining methods is linear, while nesting fills a similar purpose when we use function calls. This of course affects the way existing Swift code is written, but that is something we have to live with if we want to use familiar syntax patterns. However, I think we have to consider this difference in this case, since the syntax becomes more convoluted. Your suggestion is definitely not as easy to read as the optional chaining syntax, and maybe it can't be.

> As for how common I’d expect it to be, it’s something I’ve run into myself a few times. Again, I hope members of this list can give their view on if this would be useful to them.

I don't have any real examples, but I certainly think that I have run into it, so I'm quite open to solving the problem. For me, it is probably only a matter of finding a syntax that is acceptable.

> 3. I’m not entirely sure what the grammar situation is yet but afaik ‘?’ has never been available as a postfix operator. Perhaps I’m missing your point, could you demonstrate where it is allowed?

I did not expect that you would be able to answer that, it was more a question directed to people who are more connected to the inner workings of the parsing of Swift than I am. It is not allowed, but the error message is not the one I expect, something that gives me a hint that it does have some meaning early in the parsing.

/Magnus

_______________________________________________
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

I missed the previous threads! I’ve found one of the relevant threads here:
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160711/024201.html

Thanks for this important point.

If you were to write this logic out by hand then you would short-circuit it and this is analogous to current chaining behaviour so to me evaluating left to right (as Swift usually does) and stopping at the first failed unwrap would make sense. I wouldn’t necessarily say it’s intuitive but I don’t think it’s really less intuitive than the current chaining behaviour.

Jared

···

On 11 Dec 2017, at 19:28, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

This topic has been discussed at least two and maybe more times in the past.. It’s hard for me to post links at the moment, but it should be possible to find on Google.

One major challenge to this idea, for which no satisfactory answer has been achieved after all these years, is the following issue:

f(g()?, h()?, i(), j()?)?

If g() evaluates to nil, is h() called or not? How about i(), which is not failable? Since any function or property access can have side effects, in what order are the arguments evaluated, and how does one reason about this code flow?

To my mind, in the absence of an intuitive answer to the above—which does not appear to be possible—this idea is not feasible.
On Mon, Dec 11, 2017 at 12:34 Magnus Ahltorp via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

> 12 Dec. 2017 02:58 Jared Khan <jaredkhan@me.com <mailto:jaredkhan@me.com>> wrote:
>
> 2. It felt natural to me. It’s analogous to the existing optional chaining scenarios and composes nicely. I think it’s about as understandable as existing chaining, a newbie would have to look it up to discover its meaning. What are your thoughts on this particular syntax (ignoring 3. momentarily)? Hopefully others in this thread can share their views too.

Chaining methods is linear, while nesting fills a similar purpose when we use function calls. This of course affects the way existing Swift code is written, but that is something we have to live with if we want to use familiar syntax patterns. However, I think we have to consider this difference in this case, since the syntax becomes more convoluted. Your suggestion is definitely not as easy to read as the optional chaining syntax, and maybe it can't be.

> As for how common I’d expect it to be, it’s something I’ve run into myself a few times. Again, I hope members of this list can give their view on if this would be useful to them.

I don't have any real examples, but I certainly think that I have run into it, so I'm quite open to solving the problem. For me, it is probably only a matter of finding a syntax that is acceptable.

> 3. I’m not entirely sure what the grammar situation is yet but afaik ‘?’ has never been available as a postfix operator. Perhaps I’m missing your point, could you demonstrate where it is allowed?

I did not expect that you would be able to answer that, it was more a question directed to people who are more connected to the inner workings of the parsing of Swift than I am. It is not allowed, but the error message is not the one I expect, something that gives me a hint that it does have some meaning early in the parsing.

/Magnus

_______________________________________________
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

This is an interesting example but I think the solution might be simple if we try to desugar it.

// example
f(g()?, h()?, i(), j()?)?

// desugared
let/var someF: SomeType?
if let someG = g(), let someH = h(), let someJ = j() {
    someF = f(someG, someH, i(), someJ)
} else {
    someF = nil
}
Final syntax aside, let’s use the optional function syntax from obj-c world here for a moment: f?(g(), h(), i(), j(), k()) where the function signature would look like func f(_: G, _: H, _: I, _: J, _: K?).

Only if the parameter type is non-optional and the argument is optional then we can desugar it like this:

let/var someF: SomeType?
if let someG = g(), let someH = h(), let someJ = j() {
    someF = f(someG, someH, i(), someJ, k())
} else {
    someF = nil
}
No need to unwrap the result from k() here.

This topic has been discussed at least two and maybe more times in the past. It’s hard for me to post links at the moment, but it should be possible to find on Google.

One major challenge to this idea, for which no satisfactory answer has been achieved after all these years, is the following issue:

f(g()?, h()?, i(), j()?)?

If g() evaluates to nil, is h() called or not? How about i(), which is not failable? Since any function or property access can have side effects, in what order are the arguments evaluated, and how does one reason about this code flow?

To my mind, in the absence of an intuitive answer to the above—which does not appear to be possible—this idea is not feasible.

2. It felt natural to me. It’s analogous to the existing optional chaining scenarios and composes nicely. I think it’s about as understandable as existing chaining, a newbie would have to look it up to discover its meaning. What are your thoughts on this particular syntax (ignoring 3. momentarily)? Hopefully others in this thread can share their views too.

Chaining methods is linear, while nesting fills a similar purpose when we use function calls. This of course affects the way existing Swift code is written, but that is something we have to live with if we want to use familiar syntax patterns. However, I think we have to consider this difference in this case, since the syntax becomes more convoluted. Your suggestion is definitely not as easy to read as the optional chaining syntax, and maybe it can't be.

As for how common I’d expect it to be, it’s something I’ve run into myself a few times. Again, I hope members of this list can give their view on if this would be useful to them.

I don't have any real examples, but I certainly think that I have run into it, so I'm quite open to solving the problem. For me, it is probably only a matter of finding a syntax that is acceptable.

3. I’m not entirely sure what the grammar situation is yet but afaik ‘?’ has never been available as a postfix operator. Perhaps I’m missing your point, could you demonstrate where it is allowed?

I did not expect that you would be able to answer that, it was more a question directed to people who are more connected to the inner workings of the parsing of Swift than I am. It is not allowed, but the error message is not the one I expect, something that gives me a hint that it does have some meaning early in the parsing.

/Magnus

···

Am 11. Dezember 2017 um 20:28:52, Xiaodi Wu via swift-evolution (swift-evolution@swift.org) schrieb:
On Mon, Dec 11, 2017 at 12:34 Magnus Ahltorp via swift-evolution <swift-evolution@swift.org> wrote:

12 Dec. 2017 02:58 Jared Khan <jaredkhan@me.com> wrote:

_______________________________________________
swift-evolution mailing list
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

In my opinion, simple left to right evaluation where it short circuits on the first failure is the simplest. But I don’t think it matters that much as long as we have a consistent way to define it, and explain it.

It is nice to have forward transfer from other features, but sometimes we just need to forge ahead to create something new and then have education on the new feature.

···

On Dec 11, 2017, at 11:28 AM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

This topic has been discussed at least two and maybe more times in the past. It’s hard for me to post links at the moment, but it should be possible to find on Google.

One major challenge to this idea, for which no satisfactory answer has been achieved after all these years, is the following issue:

f(g()?, h()?, i(), j()?)?

If g() evaluates to nil, is h() called or not? How about i(), which is not failable? Since any function or property access can have side effects, in what order are the arguments evaluated, and how does one reason about this code flow?

To my mind, in the absence of an intuitive answer to the above—which does not appear to be possible—this idea is not feasible.
On Mon, Dec 11, 2017 at 12:34 Magnus Ahltorp via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

> 12 Dec. 2017 02:58 Jared Khan <jaredkhan@me.com <mailto:jaredkhan@me.com>> wrote:
>
> 2. It felt natural to me. It’s analogous to the existing optional chaining scenarios and composes nicely. I think it’s about as understandable as existing chaining, a newbie would have to look it up to discover its meaning. What are your thoughts on this particular syntax (ignoring 3. momentarily)? Hopefully others in this thread can share their views too.

Chaining methods is linear, while nesting fills a similar purpose when we use function calls. This of course affects the way existing Swift code is written, but that is something we have to live with if we want to use familiar syntax patterns. However, I think we have to consider this difference in this case, since the syntax becomes more convoluted. Your suggestion is definitely not as easy to read as the optional chaining syntax, and maybe it can't be.

> As for how common I’d expect it to be, it’s something I’ve run into myself a few times. Again, I hope members of this list can give their view on if this would be useful to them.

I don't have any real examples, but I certainly think that I have run into it, so I'm quite open to solving the problem. For me, it is probably only a matter of finding a syntax that is acceptable.

> 3. I’m not entirely sure what the grammar situation is yet but afaik ‘?’ has never been available as a postfix operator. Perhaps I’m missing your point, could you demonstrate where it is allowed?

I did not expect that you would be able to answer that, it was more a question directed to people who are more connected to the inner workings of the parsing of Swift than I am. It is not allowed, but the error message is not the one I expect, something that gives me a hint that it does have some meaning early in the parsing.

/Magnus

_______________________________________________
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

I'm not sure we need to optimize much for complicated nested examples, as I
don't think this is something that will happen a lot in practice. I imagine
the most common scenario will be a single call with one or more arguments.

Having said that, we did receive 2 different reasonable answers on what the
evaluation order would be for:
f(a(), b?)

(1) It desugars to:

if let someB = b {
f(a(), someB)
}

So a() doesn't get called if b is nil.

(2) "If you were to write this logic out by hand then you would
short-circuit it and this is analogous to current chaining behaviour so to
me evaluating left to right (as Swift usually does) and stopping at the
first failed unwrap would make sense"

So a() gets called if b is nil.

···

On Tue, Dec 12, 2017 at 12:42 AM, Slava Pestov via swift-evolution < swift-evolution@swift.org> wrote:

I think it gets confusing when you have multiple levels of nested
expressions, eg

foo(bar(x?)) + y?

Slava

I propose that we do not add a suffix ? to the arguments because it does not really signal that the whole function may return an optional value. This is especially not convenient in a nested scenario like mentioned by others in previous posts. Instead we should reuse the inifix ? on functions, subscripts and initializer. This way we can signal that the function might fail if one of the provided optional arguments could not be unwrapped where the function expects a non-optional argument. If the return type was already an optional, we do not generate a nested optional, because there is no real reason for us to do so. All arguments can simply be passed to the function with an infix ? in their current form. If all non-optional parameter types match with the argument types - optional parameter types can safely be ignored - we generate an error, thus we don’t need an infix ?, otherwise the conversion happens implicitly. This approach removes the need for explicit argument annotations, which is IMHO boilerplate in first place. It also provides a hint to the reader that some of the passed arguments should be unwrapped to succeed. In every chase the ? is always close to the parameter list ?(_:_:...).

Here are some simple examples to consider:

func first(one: A, two: B, three: C?) -> D { ... }
func second(one: A, two: B, three: C?) -> D? { ... }

···

————————————————————————————————————————————————

// error when adding infix `?` if all arguments match the types
first?(one: a, two: b, three: c) // c can be both optional and non-optional
————————————————————————————————————————————————

// no need for infix `?`
first(one: a, two: b, three: optionalC) // returns `D`
————————————————————————————————————————————————

// sugared
first?(one: optionalA, two: b, three: c) // returns `D?` if succeed

// desugared
{ () -> D? in
    if let someA = optionalA {
        return first(one: someA, two: b, three: c)
    } else {
        return nil
    }
}()
————————————————————————————————————————————————

// sugared
first?(one: optionalA, two: optionalB, three: optionalC) // returns `D?` if succeed

// desugared
{ () -> D? in
    if let someA = optionalA, let someB = optionalB {
        return first(one: someA, two: someB, three: optionalC)
    } else {
    return nil
    }
}()
————————————————————————————————————————————————

// error when adding infix `?` if all arguments match the types
second?(one: a, two: b, three: c) // c can be both optional and non-optional
————————————————————————————————————————————————

// no need for infix `?`
second(one: a, two: b, three: optionalC) // returns `D?`
————————————————————————————————————————————————

// sugared
second?(one: optionalA, two: b, three: c) // returns `D?` if succeed - no need for nested optionals!

// desugared
{ () -> D? in
    if let someA = optionalA {
        return second(one: someA, two: b, three: c)
    } else {
        return nil
    }
}()
————————————————————————————————————————————————

// sugared
second?(one: optionalA, two: optionalB, three: optionalC) // returns `D?` if succeed - no need for nested optionals!

// desugared
{ () -> D? in
    if let someA = optionalA, let someB = optionalB {
        return second(one: someA, two: someB, three: optionalC)
    } else {
        return nil
    }
}()
Please note that this can only work with non-operator functions and initializers. Operator functions cannot be referenced as normal functions yet and optional subscripts would require a source breaking change to align with that behaviour.

If I missed something where optional func from Objective-C results into incompatibility with this approach, please fell free to correct me. From my point of view I don’t see how this additional behaviour could break optional func.

Am 12. Dezember 2017 um 19:47:25, Jared Khan via swift-evolution (swift-evolution@swift.org) schrieb:

Even this small example I think this is still a little fiddly. If we add another required parameter to the Markdown initializer:
let readme = String(contentsOfFile: “README.md”).flatMap { Markdown(string: $0, flavor: .github) }
this starts to feel a little inside-out and crufty to me.

On 12 Dec 2017, at 05:54, Félix Cloutier <felixcloutier@icloud.com> wrote:

You talk about flatMap without giving an example. The readme isn't that bad with it, IMO:

if let readme = String(contentsOfFile: "README.md").flatMap(Markdown) {
// use contents here
}

That doesn't work when you need multiple optional parameters. In my own experience, that hasn't been a huge problem, though.

Félix

Le 11 déc. 2017 à 08:30, Jared Khan via swift-evolution <swift-evolution@swift.org> a écrit :

Hi all,

I'd like to propose a syntax addition that acts to ease some things that I believe should fall under the umbrella of 'optional chaining'. Optional chaining allows us to access the properties of an optional value and return nil if any link in that chain breaks. I propose we introduce syntax to allow similar chaining when passing optional valued parameters to functions that expect that parameter to be non-optional.

The example below is taken from a project I'm working on at the moment:

// Current
let readme: Markdown?
if let rawMarkdown = String(contentsOfFile: "README.md") {
        readme = Markdown(string: rawMarkdown)
} else {
        readme = nil
}
In this example we want to perform an operation, the initialisation of a 'Markdown' type, with our raw text if it exists and get nil otherwise. This is rather verbose

I propose the following syntax for an alternative:

// Proposed alternative
let readme = Markdown(string: String(contentsOfFile: "README.md")?)

The ? is familiar in its use for optional chaining.

This would act like syntactic sugar for the flatMap method on Optional. For example:

(where `john` is of a `Person` type with a property `address: Address?`)
// func getZipCode(fromAddress address: Address) -> ZipCode
getZipCode(fromAddress: john.address?)

// Would be equivalent to…
john.address.flatMap {
        getZipCode($0)
}
An example with multiple parameters:

// func getPostageEstimate(source: Address, destination: Address, weight: Double) -> Int
getPostageEstimate(source: john.address?, destination: alice.address?, weight: 2.0)

// Equivalent to
john.address.flatMap { freshVar1 in
        alice.address.flatMap { freshVar2 in
                getPostageEstimate(source: freshVar1, destination: freshVar2, weight: 2.0)
        }
}

// Or equally:
{
        guard let freshVar1 = john.address,
                let freshVar2 = alice.address else {
                        return nil
        }

        return getPostageEstimate(source: freshVar1, destination: freshVar2, weight: 2.0)
}()
This would only be allowed when the parameter doesn’t already accept Optionals and when the chained value is in fact an optional. We’d want to consider emitting at least the following errors/warnings in the given scenarios:

let result = myFunc(3?)
// error: cannot use optional chaining on non-optional value of type 'Int'

// func myFunc2(x: String?) -> String
let result = myFunc2(x: john.address?)
// error: cannot use optional argument chaining on argument of optional type
let result = myFunc(nil?)
// warning: optional argument chaining with nil literal always results in nil

Seeking your thoughts on this idea, the specific syntax, and more use case examples.

Best,

Jared

_______________________________________________
swift-evolution mailing list
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

I think evaluating them in the same way as `try` calls is consistent.

f(g()?, h()?, i(), j()?)?
// like
try f(try g(), try h(), i(), try j())
foo(bar(x()?)) + y()?
// like
foo(bar(try x())) + (try y())
···

--
Yuta

2017-12-12 7:42 GMT+09:00 Slava Pestov via swift-evolution <swift-evolution@swift.org>:

On Dec 11, 2017, at 2:41 PM, Jared Khan via swift-evolution > <swift-evolution@swift.org> wrote:

I missed the previous threads! I’ve found one of the relevant threads here:
[swift-evolution] Proposal: Extend Optional Chaining to Function, Initializer, and Subscript Parameters

Thanks for this important point.

If you were to write this logic out by hand then you would short-circuit it
and this is analogous to current chaining behaviour so to me evaluating left
to right (as Swift usually does) and stopping at the first failed unwrap
would make sense. I wouldn’t necessarily say it’s intuitive but I don’t
think it’s really less intuitive than the current chaining behaviour.

I think it gets confusing when you have multiple levels of nested
expressions, eg

foo(bar(x?)) + y?

Slava

Jared

On 11 Dec 2017, at 19:28, Xiaodi Wu via swift-evolution > <swift-evolution@swift.org> wrote:

This topic has been discussed at least two and maybe more times in the
past.. It’s hard for me to post links at the moment, but it should be
possible to find on Google.

One major challenge to this idea, for which no satisfactory answer has been
achieved after all these years, is the following issue:

f(g()?, h()?, i(), j()?)?

If g() evaluates to nil, is h() called or not? How about i(), which is not
failable? Since any function or property access can have side effects, in
what order are the arguments evaluated, and how does one reason about this
code flow?

To my mind, in the absence of an intuitive answer to the above—which does
not appear to be possible—this idea is not feasible.
On Mon, Dec 11, 2017 at 12:34 Magnus Ahltorp via swift-evolution > <swift-evolution@swift.org> wrote:

> 12 Dec. 2017 02:58 Jared Khan <jaredkhan@me.com> wrote:
>
> 2. It felt natural to me. It’s analogous to the existing optional
> chaining scenarios and composes nicely. I think it’s about as understandable
> as existing chaining, a newbie would have to look it up to discover its
> meaning. What are your thoughts on this particular syntax (ignoring 3.
> momentarily)? Hopefully others in this thread can share their views too.

Chaining methods is linear, while nesting fills a similar purpose when we
use function calls. This of course affects the way existing Swift code is
written, but that is something we have to live with if we want to use
familiar syntax patterns. However, I think we have to consider this
difference in this case, since the syntax becomes more convoluted. Your
suggestion is definitely not as easy to read as the optional chaining
syntax, and maybe it can't be.

> As for how common I’d expect it to be, it’s something I’ve run into
> myself a few times. Again, I hope members of this list can give their view
> on if this would be useful to them.

I don't have any real examples, but I certainly think that I have run into
it, so I'm quite open to solving the problem. For me, it is probably only a
matter of finding a syntax that is acceptable.

> 3. I’m not entirely sure what the grammar situation is yet but afaik ‘?’
> has never been available as a postfix operator. Perhaps I’m missing your
> point, could you demonstrate where it is allowed?

I did not expect that you would be able to answer that, it was more a
question directed to people who are more connected to the inner workings of
the parsing of Swift than I am. It is not allowed, but the error message is
not the one I expect, something that gives me a hint that it does have some
meaning early in the parsing.

/Magnus

_______________________________________________
swift-evolution mailing list
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

_______________________________________________
swift-evolution mailing list
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

2 Likes