Brainstorming: Optional sugar inferred map

Thanks, looking forward to it.

···

On 2016-01-29, at 12:04:10, Paul Ossenbruggen <possen@gmail.com> wrote:

I can take a stab at it. I will try to follow your lead and keep it short. :-)

On Jan 28, 2016, at 8:44 PM, Craig Cruden <ccruden@novafore.com <mailto:ccruden@novafore.com>> wrote:

Since the foundation of this proposal is Paul’s and yours - will either of you be drawing up the proposal?

On 2016-01-29, at 11:41:21, Jacob Bandes-Storch <jtbandes@gmail.com <mailto:jtbandes@gmail.com>> wrote:

That'd be the point. If doSomething were not optional, when "n?" appears in it, it becomes optional (it's basically optional chaining, but for function calls and other expressions instead of just dot-notation).
On Thu, Jan 28, 2016 at 8:38 PM Craig Cruden <ccruden@novafore.com <mailto:ccruden@novafore.com>> wrote:

    manager.doSomething(data: data, count: n?)

What if the return value of doSomething is not an optional? Expressions are easy — but there might be some conflicts with this one.

On 2016-01-28, at 14:34:58, Jacob Bandes-Storch via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I've wanted this sort of thing a lot. It would also work for other functions, such as

    manager.doSomething(data: data, count: n?)

which is equivalent to

    n.map { manager.doSomething(data: data, count: $0) }

It might be hard to see exactly which operator/function applications such a thing applies to, if used in the context of a complex expression.

Jacob

On Wed, Jan 27, 2016 at 9:50 PM, Paul Ossenbruggen via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Maybe something like this?

let n : Int? = 5

let r = n? + 5

On Jan 27, 2016, at 9:46 PM, Thorsten Seitz <tseitz42@icloud.com <mailto:tseitz42@icloud.com>> wrote:

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

-Thorsten

Am 28.01.2016 um 06:30 schrieb Craig Cruden via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

Yes

On 2016-01-28, at 12:28:40, Paul Ossenbruggen <possen@gmail.com <mailto:possen@gmail.com>> wrote:

Trying to see if I got this. So the type of r would be Int? at the end of this? And if n were nil then r would be nil? Otherwise it r is 10?

On Jan 27, 2016, at 9:19 PM, Craig Cruden via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Swift currently encourages a lot of conditional code - especially when it comes to optionals. In most cases when you are computing etc. on an Optional you would expect that you would want an optional result and things to be able to use optionals.

In another language I generally just `map` one optional to another - which may not be the most readable code to some not use to optionals.

I was wondering if maybe an expression is not available that it would rewrite the syntax to map from one to another value.

So things like:

let n : Int? = 5

let r = n + 5

would actually compile as

let r = n.map {$0 + 5}
_______________________________________________
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 <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

Another conundrum: are the optional-pattern arguments evaluated before any
other arguments? After?

···

On Fri, Jan 29, 2016 at 9:54 AM Paul Ossenbruggen <possen@gmail.com> wrote:

Yeah I was thinking about that too, multiple optionals. It gets a bit
confusing as to the scope.

Wondering if we should pass it on as an optional or not allow it?
Something else?

Sent from my iPhone

On Jan 29, 2016, at 9:26 AM, Jacob Bandes-Storch <jtbandes@gmail.com> > wrote:

Hi Paul,
I think the two statements would behave separately (as if you had used map
twice).

If you want them to act together, you could do this:

{ manager.doSomething(data: data, count: $0); doSomethingElse($0) }(n?)

Another aspect worth considering is whether this syntax should work with
multiple optionals used in the same expression. (e.g. if they are all
non-nil, the expression is evaluated.)
On Fri, Jan 29, 2016 at 1:53 AM Paul Ossenbruggen <possen@gmail.com> > wrote:

Jacob,

While working on proposal, I am just trying to understand one thing with
this. In your second example using map, it is clear where the block of code
begins. How would you deal with multiple statements or ones that returned
no value:

    manager.doSomething(data: data, count: n?); doSomethingElse( n?)

the equivalent with map is

n.map { manager.doSomething(data: data, count: $0); doSomethingElse($0) }

Thanks,
- Paul

On Jan 28, 2016, at 8:41 PM, Jacob Bandes-Storch <jtbandes@gmail.com> >> wrote:

That'd be the point. If doSomething were not optional, when "n?" appears
in it, it becomes optional (it's basically optional chaining, but for
function calls and other expressions instead of just dot-notation).
On Thu, Jan 28, 2016 at 8:38 PM Craig Cruden <ccruden@novafore.com> >> wrote:

    manager.doSomething(data: data, count: n?)

What if the return value of doSomething is not an optional? Expressions
are easy — but there might be some conflicts with this one.

On 2016-01-28, at 14:34:58, Jacob Bandes-Storch via swift-evolution < >>> swift-evolution@swift.org> wrote:

I've wanted this sort of thing a lot. It would also work for other
functions, such as

    manager.doSomething(data: data, count: n?)

which is equivalent to

    n.map { manager.doSomething(data: data, count: $0) }

It might be hard to see exactly which operator/function applications
such a thing applies to, if used in the context of a complex expression.

Jacob

On Wed, Jan 27, 2016 at 9:50 PM, Paul Ossenbruggen via swift-evolution < >>> swift-evolution@swift.org> wrote:

Maybe something like this?

let n : Int? = 5

let r = n? + 5

On Jan 27, 2016, at 9:46 PM, Thorsten Seitz <tseitz42@icloud.com> >>>> wrote:

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

-Thorsten

Am 28.01.2016 um 06:30 schrieb Craig Cruden via swift-evolution < >>>> swift-evolution@swift.org>:

Yes

On 2016-01-28, at 12:28:40, Paul Ossenbruggen <possen@gmail.com> wrote:

Trying to see if I got this. So the type of r would be Int? at the end
of this? And if n were nil then r would be nil? Otherwise it r is 10?

On Jan 27, 2016, at 9:19 PM, Craig Cruden via swift-evolution < >>>> swift-evolution@swift.org> wrote:

Swift currently encourages a lot of conditional code - especially when
it comes to optionals. In most cases when you are computing etc. on an
Optional you would expect that you would want an optional result and things
to be able to use optionals.

In another language I generally just `map` one optional to another -
which may not be the most readable code to some not use to optionals.

I was wondering if maybe an expression is not available that it would
rewrite the syntax to map from one to another value.

So things like:

let n : Int? = 5

let r = n + 5

would actually compile as

let r = n.map {$0 + 5}
_______________________________________________
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

I don’t think I can go any further on the proposal until this is worked out. Any ideas or feedback would be welcome.

Options so far are:
• Only add simple expressions use map for cases where code is conditionally executed after the map
• Add simple expressions and delimited approach for cases where the code is conditionally executed after the map.
• Some other ideas
• Forget about the proposal.

···

On Jan 30, 2016, at 9:24 PM, Paul Ossenbruggen <possen@gmail.com> wrote:

This is what I came up with so far. As Joe points out, it needs delimiters for more complex maps. Assume this for the examples:

func doSomething(value: Int) -> Int {
    return value
}
let pf : Int? = 5
let pg : Int? = nil

for simple expressions you could do this which is a pretty big win:

let gf = pf? + 5

For cases where the expression should not call a function if a parameter is nil, I came up with this for syntactic sugar which has delimiters. Inside the brackets the variable name is unwrapped automatically and real name of the variable is used. rather than $0. It is a win over the if let approach:

// if let approach
let ff : Int? = nil
if let pf = pf {
ff = doSomething(pf)
}

// the delimited approach is:
let gf = pf?{ doSomething(pf) } // pf not nil doSomething is called
let gg = pg?{ doSomething(pg) } // pg is nil doSomething is not called.

gf -> 5
gg -> nil

// which is the equivalent of the map approach:
let ff = pf.map { doSomething($0) }
let fg = pg.map { doSomething($0) }

ff -> 5
fg -> nil

Assuming that this syntax is not a big enough win over the map version, This means that in this situation, you need to use map.. If doSomething() took an optional though you could do this, because doSomething would always be called.

let gf = doSomething(pf?) // result is optional, could also omit “?"
let hf = doSomething(pf? + 5) // result is still an optional

- Paul

On Jan 30, 2016, at 10:28 AM, Joe Groff <jgroff@apple.com <mailto:jgroff@apple.com>> wrote:

On Jan 27, 2016, at 11:34 PM, Jacob Bandes-Storch via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I've wanted this sort of thing a lot. It would also work for other functions, such as

   manager.doSomething(data: data, count: n?)

which is equivalent to

   n.map { manager.doSomething(data: data, count: $0) }

It might be hard to see exactly which operator/function applications such a thing applies to, if used in the context of a complex expression.

Yes, this would be a major problem with this sort of feature. Without some explicit delimiter for the bounds of the map operation, you have either extremely subtle rules for defining the implicit bounds, or an exponential type-checking problem figuring it out from context.

-Joe

I would think the extra delimiters would just make it hairy — so if it were delimiter or not being able to be done I would think you would assign an intermediary value to another let.

i.e.

let n = pf? + 5
let hf = doSomething(n?)

···

That being said I don’t see why their could not be a rule for inferred unwrapping. So if a function parameter was defined as Int and it was passed Some(Int) it would execute, but if it were None/Nil then it would skip execution.

On 2016-01-31, at 12:24:03, Paul Ossenbruggen via swift-evolution <swift-evolution@swift.org> wrote:

This is what I came up with so far. As Joe points out, it needs delimiters for more complex maps. Assume this for the examples:

func doSomething(value: Int) -> Int {
    return value
}
let pf : Int? = 5
let pg : Int? = nil

for simple expressions you could do this which is a pretty big win:

let gf = pf? + 5

For cases where the expression should not call a function if a parameter is nil, I came up with this for syntactic sugar which has delimiters. Inside the brackets the variable name is unwrapped automatically and real name of the variable is used. rather than $0. It is a win over the if let approach:

// if let approach
let ff : Int? = nil
if let pf = pf {
ff = doSomething(pf)
}

// the delimited approach is:
let gf = pf?{ doSomething(pf) } // pf not nil doSomething is called
let gg = pg?{ doSomething(pg) } // pg is nil doSomething is not called.

gf -> 5
gg -> nil

// which is the equivalent of the map approach:
let ff = pf.map { doSomething($0) }
let fg = pg.map { doSomething($0) }

ff -> 5
fg -> nil

Assuming that this syntax is not a big enough win over the map version, This means that in this situation, you need to use map.. If doSomething() took an optional though you could do this, because doSomething would always be called.

let gf = doSomething(pf?) // result is optional, could also omit “?"
let hf = doSomething(pf? + 5) // result is still an optional

- Paul

On Jan 30, 2016, at 10:28 AM, Joe Groff <jgroff@apple.com <mailto:jgroff@apple.com>> wrote:

On Jan 27, 2016, at 11:34 PM, Jacob Bandes-Storch via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I've wanted this sort of thing a lot. It would also work for other functions, such as

   manager.doSomething(data: data, count: n?)

which is equivalent to

   n.map { manager.doSomething(data: data, count: $0) }

It might be hard to see exactly which operator/function applications such a thing applies to, if used in the context of a complex expression.

Yes, this would be a major problem with this sort of feature. Without some explicit delimiter for the bounds of the map operation, you have either extremely subtle rules for defining the implicit bounds, or an exponential type-checking problem figuring it out from context.

-Joe

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

I would think it would be functionally equivalent to Scala.

for {
   o1 <- optional1
   o2 <- optional2
   ..
} yield { manager.doSumething(data, o1, o2) }

which is a combination of maps and flatMaps.

···

On 2016-01-30, at 1:06:00, Jacob Bandes-Storch <jtbandes@gmail.com> wrote:

Another conundrum: are the optional-pattern arguments evaluated before any other arguments? After?
On Fri, Jan 29, 2016 at 9:54 AM Paul Ossenbruggen <possen@gmail.com <mailto:possen@gmail.com>> wrote:
Yeah I was thinking about that too, multiple optionals. It gets a bit confusing as to the scope.

Wondering if we should pass it on as an optional or not allow it? Something else?

Sent from my iPhone

On Jan 29, 2016, at 9:26 AM, Jacob Bandes-Storch <jtbandes@gmail.com <mailto:jtbandes@gmail.com>> wrote:

Hi Paul,
I think the two statements would behave separately (as if you had used map twice).

If you want them to act together, you could do this:

{ manager.doSomething(data: data, count: $0); doSomethingElse($0) }(n?)

Another aspect worth considering is whether this syntax should work with multiple optionals used in the same expression. (e.g. if they are all non-nil, the expression is evaluated.)
On Fri, Jan 29, 2016 at 1:53 AM Paul Ossenbruggen <possen@gmail.com <mailto:possen@gmail.com>> wrote:
Jacob,

While working on proposal, I am just trying to understand one thing with this. In your second example using map, it is clear where the block of code begins. How would you deal with multiple statements or ones that returned no value:

      manager.doSomething(data: data, count: n?); doSomethingElse( n?)

the equivalent with map is

  n.map { manager.doSomething(data: data, count: $0); doSomethingElse($0) }

Thanks,
- Paul

On Jan 28, 2016, at 8:41 PM, Jacob Bandes-Storch <jtbandes@gmail.com <mailto:jtbandes@gmail.com>> wrote:

That'd be the point. If doSomething were not optional, when "n?" appears in it, it becomes optional (it's basically optional chaining, but for function calls and other expressions instead of just dot-notation).
On Thu, Jan 28, 2016 at 8:38 PM Craig Cruden <ccruden@novafore.com <mailto:ccruden@novafore.com>> wrote:

    manager.doSomething(data: data, count: n?)

What if the return value of doSomething is not an optional? Expressions are easy — but there might be some conflicts with this one.

On 2016-01-28, at 14:34:58, Jacob Bandes-Storch via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I've wanted this sort of thing a lot. It would also work for other functions, such as

    manager.doSomething(data: data, count: n?)

which is equivalent to

    n.map { manager.doSomething(data: data, count: $0) }

It might be hard to see exactly which operator/function applications such a thing applies to, if used in the context of a complex expression.

Jacob

On Wed, Jan 27, 2016 at 9:50 PM, Paul Ossenbruggen via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Maybe something like this?

let n : Int? = 5

let r = n? + 5

On Jan 27, 2016, at 9:46 PM, Thorsten Seitz <tseitz42@icloud.com <mailto:tseitz42@icloud.com>> wrote:

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

-Thorsten

Am 28.01.2016 um 06:30 schrieb Craig Cruden via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

Yes

On 2016-01-28, at 12:28:40, Paul Ossenbruggen <possen@gmail.com <mailto:possen@gmail.com>> wrote:

Trying to see if I got this. So the type of r would be Int? at the end of this? And if n were nil then r would be nil? Otherwise it r is 10?

On Jan 27, 2016, at 9:19 PM, Craig Cruden via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Swift currently encourages a lot of conditional code - especially when it comes to optionals. In most cases when you are computing etc. on an Optional you would expect that you would want an optional result and things to be able to use optionals.

In another language I generally just `map` one optional to another - which may not be the most readable code to some not use to optionals.

I was wondering if maybe an expression is not available that it would rewrite the syntax to map from one to another value.

So things like:

let n : Int? = 5

let r = n + 5

would actually compile as

let r = n.map {$0 + 5}
_______________________________________________
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 <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

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

Non-Conditional Optional Unwrapping

Proposal: SE- <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md&gt;TBD
Author(s): Paul Ossenbruggen, Craig Cruden
Status: Awaiting review
Review manager: TBD
<GitHub - apple/swift-evolution: This maintains proposals for changes and user-visible enhancements to the Swift Programming Language.

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

     let a : Int? = 10
  let b = 5

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

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

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

<GitHub - apple/swift-evolution: This maintains proposals for changes and user-visible enhancements to the Swift Programming Language.

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

<GitHub - apple/swift-evolution: This maintains proposals for changes and user-visible enhancements to the Swift Programming Language. solution

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

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

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

a? + b

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

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

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

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

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

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

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

ra -> 5
rc -> nil

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

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

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

ra -> 5
rc -> nil

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

func doSomethingElse(value1: Int, value2: Int) -> Void {
    return value1 + value2
}
let ra = a ?? {
    doSomething($$)
    doSomethingElse($$, value2: b)
}

multiple optionals would work utilizing a tuple that could address elements using the $n syntax:

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

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

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

<Not Complete>

<GitHub - apple/swift-evolution: This maintains proposals for changes and user-visible enhancements to the Swift Programming Language. on existing code

Existing code would not be affected by this change.

<GitHub - apple/swift-evolution: This maintains proposals for changes and user-visible enhancements to the Swift Programming Language. considered

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

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

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

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

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

The for translates into:

o1.flatMap(x => o2.map(y => manager.doSumething(data, x, y)))

···

On 2016-01-30, at 6:15:18, Craig Cruden <ccruden@novafore.com> wrote:

I would think it would be functionally equivalent to Scala.

for {
   o1 <- optional1
   o2 <- optional2
   ..
} yield { manager.doSumething(data, o1, o2) }

which is a combination of maps and flatMaps.

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

a? + b

FWIW, "a?” is the degenerate form of the optional chaining operation, which tests A, conditionally extracts its value, then re-wraps it back up without performing any other operations on it. It is equivalent to “a?.identity()”. The compiler rejects it because it is confusing/surprising for people, not because it is undefined.

I would be very concerned with defining this operation to mean something else, because that introduces inconsistency and a completely different kind of surprise to the language.

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

Just to point one thing out, ?? is an operator in the stdlib. Doing something like this would require baking it into the compiler.

IMO, the problem you’re addressing is already adequately solved by guard.

-Chris

···

On Feb 15, 2016, at 12:49 AM, Paul Ossenbruggen via swift-evolution <swift-evolution@swift.org> wrote:

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

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

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

ra -> 5
rc -> nil

I think this is completely the wrong way to approach this.

*If* you want to implement this feature, I think the way to do it is to say that you can put ? after any optional parameter to a normal function call, and Swift will unwrap all the parameters so marked and make the call return `nil` if any of them are nil.

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

Naturally, since operators are just function calls with a funny syntax, this would also extend to operators.

  a? + b // a.k.a. +(a?, b)

Even in this form, however, I don't think this is a feature worth having. Too indirect and specialized.

···

--
Brent Royal-Gordon
Architechies

This is all still in the brainstorming realm. The issue is that doSomething looks like it should always be called. I think that the nil coalescing operator works here:

func doSomething(value: Int) -> Int {
    return value
}
let pf : Int? = 5
let py : Int? = nil

// the following is:
let gf = pf ?? doSomething(pf!)
let gy = py ?? doSomething(py!)

gf -> 5
gy -> nil

// is the equivalent of:
let ff = pf.map { doSomething($0) }
let fy = py.map { doSomething($0) }

ff -> 5
fy -> nil

This works with the language as it stands today but I don’t like the forced unwrap. However, with the rest of the proposal this would work because pf? would give you the unwrapped value, which would not be nil at that point.

let gf = pf ?? doSomething(pf?)
let gy = py ?? doSomething(py?)

But one outstanding issue is that gf and gy would not be optional unless you explicitly write out the type like this.

let gf : Int? = pf ?? doSomething(pf?)
let gy : Int? = py ?? doSomething(py?)

I think this is in line with the language as it is today. Unless we want to add a new sugar to upgrade an optional

let gf? = pf ?? doSomething(pf?)
let gy? = py ?? doSomething(py?)

where if there is a let or var assignment with a question mark the type is upgraded to an optional.

···

------

Moving back to the more complex example. The more complex example would require another construct. We would need to have a way to add a block.for nil coalescing but pretty sure that would not be approved and it probably creates other problems. Assuming that doSomething else is a void function.

  let dd = n ?? { manager.doSomething(data: data, count: n!); doSomethingElse( n! ) }

So without adding more constructs you would have to do this for the more complex example:

   let dd = n ?? manager.doSomething(data: data, count: n?)
   if let n = n {
     doSomethingElse( n )
  }

In which case you might as well do this:

   let dd : Int? = nil
   if let n = n {
   dd = manager.doSomething(data: data, count: n)
      doSomethingElse( n )
  }

But I think the more complex example will be rare and the last example is fine should you have a complex situation

Another conundrum: are the optional-pattern arguments evaluated before any other arguments? After?

I think the two statements would behave separately (as if you had used map twice).

If you want them to act together, you could do this:

{ manager.doSomething(data: data, count: $0); doSomethingElse($0) }(n?)

Another aspect worth considering is whether this syntax should work with multiple optionals used in the same expression. (e.g. if they are all non-nil, the expression is evaluated.)

-1 from me as it means that a complex statement with a single question marked variable could in fact be an entirely optional statement. While I dislike the boilerplate as well, I think it’s much better to have the explicit conditional branch there to make it absolutely clear that a statement is being executed in one case and not in another.

I appreciate the intent of the proposal though, I just think it’s not the right solution as it could make code less readable as I feel it’s better to be explicit with cases such as these IMO.

Also, I don’t like the nil-coalescing operator changes, as it seems to contradict the normal form where the right hand side is the fallback value, not the intended result. It’d be better to have some kind of non-nil operator, but personally I don’t think it’s worth it.

···

On 15 Feb 2016, at 17:45, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 15, 2016, at 12:49 AM, Paul Ossenbruggen via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

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

a? + b

FWIW, "a?” is the degenerate form of the optional chaining operation, which tests A, conditionally extracts its value, then re-wraps it back up without performing any other operations on it. It is equivalent to “a?.identity()”. The compiler rejects it because it is confusing/surprising for people, not because it is undefined.

I would be very concerned with defining this operation to mean something else, because that introduces inconsistency and a completely different kind of surprise to the language.

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

Just to point one thing out, ?? is an operator in the stdlib. Doing something like this would require baking it into the compiler.

IMO, the problem you’re addressing is already adequately solved by guard.

-Chris

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

a? + b

FWIW, "a?” is the degenerate form of the optional chaining operation, which tests A, conditionally extracts its value, then re-wraps it back up without performing any other operations on it. It is equivalent to “a?.identity()”. The compiler rejects it because it is confusing/surprising for people, not because it is undefined.

Thanks for explaining that, that it is doing an identity and that is not undefined. I certainly missed that. .

I would be very concerned with defining this operation to mean something else, because that introduces inconsistency and a completely different kind of surprise to the language.

I am sorry, I am not getting why this is meaning something else, confusing or inconsistent, seems more consistent because you are returning an optional and that would not surprise me, it seems more surprising that you get an error. if it was already an optional and the expression as a whole results in a an optional, which is similar to what happens with optional chaining. With optional chaining, I expect it to return an optional as well, to me this seems very similar. If a is an optional, it would not be surprising for a? to return an optional. For example,

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

Just to point one thing out, ?? is an operator in the stdlib. Doing something like this would require baking it into the compiler.

I guess if auto closure could take a parameter you could do something like this:

@warn_unused_result
public func ??<T>(optional: T?, @autoclosure defaultValue: (result : T) throws -> T) rethrows -> T {
    return optional != nil ? optional! : try defaultValue(result: optional!)
}

I am not an expert on this though. I would acknowledge that to the later examples with multiple statements may be harder to do.

···

On Feb 15, 2016, at 9:45 AM, Chris Lattner <clattner@apple.com> wrote:
On Feb 15, 2016, at 12:49 AM, Paul Ossenbruggen via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

IMO, the problem you’re addressing is already adequately solved by guard.

-Chris

But wouldn’t you say optional chaining is almost the same thing? I’m a bit puzzled why people say this new proposal makes the code confusing, while the current implementation of optional chaining isn’t frowned upon.

let a: String? = "123"
let b: String = "456"
let c = a?.stringByAppendingString(b)

This works as expected.

let a: String = "123"
let b: String? = "456"
let c = a.stringByAppendingString(b?)

This, however, does not work. I think the only reason the second example seems a bit off is because we’re not used to it, but both seem equally readable to me. The second example is basically equivalent to this:

let a: String = “123”
let b: String? = “456”
let c = b?.stringByPrependingString(a)

Which would work, if only there is no such function. I guess the point I’m trying to make is that currently, whether we have to use a lot of boilerplate code depends on the choice of receiver and argument of the function, which could be completely arbitrary.

···

> To address this, the nil-coalescing operator would allow $$, where $$ is the unwrapped unnamed result of the expression when non nil:
>
> func doSomething(value: Int) ->Int {
> return value
> }
>
> let ra = a ?? doSomething($$)
> let rc = c ?? doSomething($$)
>
> ra ->5
> rc ->nil
I think this is completely the wrong way to approach this.

*If* you want to implement this feature, I think the way to do it is to say that you can put ? after any optional parameter to a normal function call, and Swift will unwrap all the parameters so marked and make the call return `nil` if any of them are nil.

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

Naturally, since operators are just function calls with a funny syntax, this would also extend to operators.

a? + b // a.k.a. +(a?, b)

Even in this form, however, I don't think this is a feature worth having. Too indirect and specialized.

--
Brent Royal-Gordon
Architechies

-1 from me as it means that a complex statement with a single question marked variable could in fact be an entirely optional statement. While I dislike the boilerplate as well, I think it’s much better to have the explicit conditional branch there to make it absolutely clear that a statement is being executed in one case and not in another.

We frequently use optional chaining, where you get an optional result. How is this different?

I appreciate the intent of the proposal though, I just think it’s not the right solution as it could make code less readable as I feel it’s better to be explicit with cases such as these IMO.

I am puzzled as to how it is less readable than the examples using “if let” and map, this seems far more readable to me.

Also, I don’t like the nil-coalescing operator changes, as it seems to contradict the normal form where the right hand side is the fallback value, not the intended result. It’d be better to have some kind of non-nil operator, but personally I don’t think it’s worth it.

For nil-coalescing changes, while I see your point, i don’t see why we should constrain it to that usage model only, if it can be made more powerful and easier to read than map. This is essentially syntactic sugar for map on an optional. Currently it is possible to do this:

a ?? doSomething(59)

it is not a huge stretch to allow the result of the left hand optional expression on the right hand side:

      a ?? doSomething($$)

The more advanced things like multiple statements in closure, or tuple are certainly up for discussion or could even be a future addition, if there is demand.

···

On Feb 15, 2016, at 12:22 PM, Haravikk <swift-evolution@haravikk.me> wrote:

Oops, I had an example and decided to delete it.

···

On Feb 15, 2016, at 8:52 PM, Paul Ossenbruggen <possen@gmail.com> wrote:

On Feb 15, 2016, at 9:45 AM, Chris Lattner <clattner@apple.com <mailto:clattner@apple.com>> wrote:

On Feb 15, 2016, at 12:49 AM, Paul Ossenbruggen via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

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

a? + b

FWIW, "a?” is the degenerate form of the optional chaining operation, which tests A, conditionally extracts its value, then re-wraps it back up without performing any other operations on it. It is equivalent to “a?.identity()”. The compiler rejects it because it is confusing/surprising for people, not because it is undefined.

Thanks for explaining that, that it is doing an identity and that is not undefined. I certainly missed that. .

I would be very concerned with defining this operation to mean something else, because that introduces inconsistency and a completely different kind of surprise to the language.

I am sorry, I am not getting why this is meaning something else, confusing or inconsistent, seems more consistent because you are returning an optional and that would not surprise me, it seems more surprising that you get an error. if it was already an optional and the expression as a whole results in a an optional, which is similar to what happens with optional chaining. With optional chaining, I expect it to return an optional as well, to me this seems very similar. If a is an optional, it would not be surprising for a? to return an optional. For example,

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

Just to point one thing out, ?? is an operator in the stdlib. Doing something like this would require baking it into the compiler.

I guess if auto closure could take a parameter you could do something like this:

@warn_unused_result
public func ??<T>(optional: T?, @autoclosure defaultValue: (result : T) throws -> T) rethrows -> T {
    return optional != nil ? optional! : try defaultValue(result: optional!)
}

I am not an expert on this though. I would acknowledge that to the later examples with multiple statements may be harder to do.

IMO, the problem you’re addressing is already adequately solved by guard.

-Chris

This will always throw an exception as you are force unwrapping the optional in both branches of the ternary, i.e. also in the branch which is used when it is nil.

-Thorsten

···

Am 16.02.2016 um 05:52 schrieb Paul Ossenbruggen via swift-evolution <swift-evolution@swift.org>:

I guess if auto closure could take a parameter you could do something like this:

@warn_unused_result
public func ??<T>(optional: T?, @autoclosure defaultValue: (result : T) throws -> T) rethrows -> T {
    return optional != nil ? optional! : try defaultValue(result: optional!)
}

But wouldn’t you say optional chaining is almost the same thing? I’m a bit puzzled why people say this new proposal makes the code confusing, while the current implementation of optional chaining isn’t frowned upon.

My version is definitely completely analogous to optional chaining; you're just doing it on the optionality of the parameters instead of the method's target.

But just because it's analogous doesn't mean it's a good idea.

To see why, let's dispose of the "a" and "b" examples and do something a little more realistic. This code is based on something one of my controllers does (though I've intentionally complicated it). Give it a glance, but don't linger on it:

  rebuildPageList(in: self.pagesPopupButton?.menu?, from: self.book?.tableOfContents.elements ?? )

Does rebuildPageList(in:from:) get called every time this line is run? Does it depend on the popup button, the element list, or both? Which parts of the method chain that creates each parameter can be nil, and which cannot? If you carefully parse the line, you can answer all these questions, but it's not too easy at a glance.

This is less of a problem for the current optional chaining features because they all work in a line, so to speak; you can ignore the parameters and keys and merely focus on the chain of methods, properties, and subscriptors.

It's not that this isn't a straightforward extension of an existing idea—it absolutely is. But sometimes a syntax that's perfect for one area is too lightweight, too easy to overlook, for another. With this proposal in place, the only way to know if any function, or even any *operator*, is actually performed is to check all of its parameters for a question mark. That may be a bridge too far.

And again, I'm not convinced that there are *that* many cases where you want a function to return nil instead of being executed if one of its parameters is nil. It certainly happens sometimes—it's even happened to me on occasion—but I'm not sure it's a common enough case, particularly when the proposed solution is "add a feature that means the presence or absence of a single character buried in the middle of complex expression can make it conditional".

In short, your proposal will require people to read their code *much* more closely than they currently need to. I'm not sure it offers enough benefit to impose that cost.

···

--
Brent Royal-Gordon
Architechies

If ‘b’ were nil then the result of appending it to a String would be nil. I think this would cause quite a bit of confusion (an appending operation resulting in less than what you started with).

The equivalent code would be:

b.map { a.stringByAppendingString($0) }

It might not be as succinct, but it makes it clearer that the operation depends on the value of ‘b’.

···

On 16 Feb 2016, at 14:10, tvermeulen via swift-evolution <swift-evolution@swift.org> wrote:

let a: String = "123"
let b: String? = "456"
let c = a.stringByAppendingString(b?)

This, however, does not work.

Optional chaining is different because of the asymmetry between receiver and arguments (Swift is using single dispatch and not multiple dispatch) and moreover because it is linear and the execution order is clear and simple to understand, i.e. when evaluating

a?.foo()?.bar()?.baz()

I know that when bar() answers nil that foo() has been executed but baz() has not.

When evaluating

a.foo()? + b.bar()? * baz(c?)

I think is is much more difficult to understand what will have been executed when c answers nil, for example.

-Thorsten

···

Am 16.02.2016 um 15:10 schrieb tvermeulen via swift-evolution <swift-evolution@swift.org>:

But wouldn’t you say optional chaining is almost the same thing? I’m a bit puzzled why people say this new proposal makes the code confusing, while the current implementation of optional chaining isn’t frowned upon.

let a: String? = "123"
let b: String = "456"
let c = a?.stringByAppendingString(b)

This works as expected.

let a: String = "123"
let b: String? = "456"
let c = a.stringByAppendingString(b?)

This, however, does not work. I think the only reason the second example seems a bit off is because we’re not used to it, but both seem equally readable to me. The second example is basically equivalent to this:

let a: String = “123”
let b: String? = “456”
let c = b?.stringByPrependingString(a)

Which would work, if only there is no such function. I guess the point I’m trying to make is that currently, whether we have to use a lot of boilerplate code depends on the choice of receiver and argument of the function, which could be completely arbitrary.

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

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

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

ra ->5
rc ->nil

I think this is completely the wrong way to approach this.

*If* you want to implement this feature, I think the way to do it is to say that you can put ? after any optional parameter to a normal function call, and Swift will unwrap all the parameters so marked and make the call return `nil` if any of them are nil.

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

Naturally, since operators are just function calls with a funny syntax, this would also extend to operators.

a? + b // a.k.a. +(a?, b)

Even in this form, however, I don't think this is a feature worth having. Too indirect and specialized.

--
Brent Royal-Gordon
Architechies

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

Yeah, sorry did that kind of quick. Just trying to express the idea, maybe my implementation left something to be desired. I wanted the result not the optional.

···

On Feb 15, 2016, at 10:35 PM, Thorsten Seitz <tseitz42@icloud.com> wrote:

Am 16.02.2016 um 05:52 schrieb Paul Ossenbruggen via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

I guess if auto closure could take a parameter you could do something like this:

@warn_unused_result
public func ??<T>(optional: T?, @autoclosure defaultValue: (result : T) throws -> T) rethrows -> T {
    return optional != nil ? optional! : try defaultValue(result: optional!)
}

This will always throw an exception as you are force unwrapping the optional in both branches of the ternary, i.e. also in the branch which is used when it is nil.

-Thorsten