ternary operator ?: suggestion

+Doug for his thoughts on the web page:

Thank you Chris for all the feedback.

To be clear, I’m not completely opposed to a change here, but it has been discussed extensively before, and didn’t turn up any good ideas either. That doesn’t mean that a good idea isn’t out there - it might be a very hard problem (of course, it really might be that ?: cannot be beat just because it is established, and anything as good as it but different would be considered “weird and not better”).

Should there be a spot on the website or github with frequent proposed changes that are not feasible or that not better solution has been found (such as the ?: operator )?

I am thinking this would cut down on people asking to turn operators into keywords (or similar ) but still encourage better solutions to be proposed.

I think it is a really interesting idea to have a list of “commonly proposed changes” on the Swift evolution web page, with a link to the discussion archives for it. While it shouldn’t be verboten to explore an area in the future, anyone doing so can reasonably be expected to read the previous discussions and only reopen it if there is new information or a new idea.

What do you think Doug?

-Chris

···

On Dec 16, 2015, at 10:01 PM, J. Cheyo Jimenez <cheyo@masters3d.com> wrote:

On Wednesday, December 16, 2015, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

> On Dec 16, 2015, at 7:36 PM, Charles Constant <charles@charlesism.com <javascript:;>> wrote:
>
> One more thought. This syntax would also placate ternary haters. I think the traditional complaint about ternary expressions is that people can't remember the order of the true and false values. This would not be an issue with:
>
> let i = boo ? ( true: 123, false: 456 )

I would characterize this as “different” than ?:, but not better. Given that it isn’t “better”, I’d argue that following C (and tons of other languages) would make sense here.

-Chris

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

My reason for suggesting the change is that it would be nice to have a one liner for non-boolean values. Especially when the mapping isn't likely to be reused anyplace else. I run into this a fair amount, and the inline way I do it (via a Dict) requires a lot of verbosity (keys are likely to be Enums and need to be explicitly declared for the Dict).

You can improve the ergonomics of this with a custom operator:

  infix operator ??? {} // Used because ? is reserved; in practice, you should use something else.
  
  func ???<Key, Value>(key: Key, dictionary: [Key: Value]) -> Value {
      return dictionary[key]!
  }
  
  let alignment = NSTextAlignment.Left
  alignment ??? [.Left: "Left", .Right: "Right", .Center: "Center", .Justified: "Justified"]

Actually, this *almost* does what you want. No @autoclosure for the values and no exhaustiveness checking, but otherwise...

···

--
Brent Royal-Gordon
Architechies

that is cool thanks!

···

On Dec 16, 2015, at 11:09 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

My reason for suggesting the change is that it would be nice to have a one liner for non-boolean values. Especially when the mapping isn't likely to be reused anyplace else. I run into this a fair amount, and the inline way I do it (via a Dict) requires a lot of verbosity (keys are likely to be Enums and need to be explicitly declared for the Dict).

You can improve the ergonomics of this with a custom operator:

  infix operator ??? {} // Used because ? is reserved; in practice, you should use something else.
  
  func ???<Key, Value>(key: Key, dictionary: [Key: Value]) -> Value {
      return dictionary[key]!
  }
  
  let alignment = NSTextAlignment.Left
  alignment ??? [.Left: "Left", .Right: "Right", .Center: "Center", .Justified: "Justified"]

Actually, this *almost* does what you want. No @autoclosure for the values and no exhaustiveness checking, but otherwise...

--
Brent Royal-Gordon
Architechies

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

I support that idea too. I would love to read what had been discussed before.

···

On Dec 16, 2015, at 10:44 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

I think it is a really interesting idea to have a list of “commonly proposed changes” on the Swift evolution web page, with a link to the discussion archives for it. While it shouldn’t be verboten to explore an area in the future, anyone doing so can reasonably be expected to read the previous discussions and only reopen it if there is new information or a new idea.

What do you think Doug?

I think it’s a good idea, which we can incorporate in the swift-evolution repository. My main concern is that I (personally), and the core team in general, probably won’t be able to proactively maintain such a list. We’d happily accept pull requests to help keep the list up-to-date and (as it grows longer) give it some organization.

  - Doug

···

On Dec 16, 2015, at 10:44 PM, Chris Lattner <clattner@apple.com> wrote:

+Doug for his thoughts on the web page:

On Dec 16, 2015, at 10:01 PM, J. Cheyo Jimenez <cheyo@masters3d.com <mailto:cheyo@masters3d.com>> wrote:
Should there be a spot on the website or github with frequent proposed changes that are not feasible or that not better solution has been found (such as the ?: operator )?

I am thinking this would cut down on people asking to turn operators into keywords (or similar ) but still encourage better solutions to be proposed.

I think it is a really interesting idea to have a list of “commonly proposed changes” on the Swift evolution web page, with a link to the discussion archives for it. While it shouldn’t be verboten to explore an area in the future, anyone doing so can reasonably be expected to read the previous discussions and only reopen it if there is new information or a new idea.

What do you think Doug?

No exhaustiveness checking is a serious deficiency :-(

-Thorsten

···

Am 17.12.2015 um 08:09 schrieb Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org>:

Actually, this *almost* does what you want. No @autoclosure for the values and no exhaustiveness checking, but otherwise...

Ok, please carve out a logical place on the swift-evolution page to put these things (probably near the end, or on a separate page?) and we can fill it in over time. Thanks Doug!

-Chris

···

On Dec 17, 2015, at 9:38 AM, Douglas Gregor <dgregor@apple.com> wrote:

On Dec 16, 2015, at 10:44 PM, Chris Lattner <clattner@apple.com <mailto:clattner@apple.com>> wrote:

+Doug for his thoughts on the web page:

On Dec 16, 2015, at 10:01 PM, J. Cheyo Jimenez <cheyo@masters3d.com <mailto:cheyo@masters3d.com>> wrote:
Should there be a spot on the website or github with frequent proposed changes that are not feasible or that not better solution has been found (such as the ?: operator )?

I am thinking this would cut down on people asking to turn operators into keywords (or similar ) but still encourage better solutions to be proposed.

I think it is a really interesting idea to have a list of “commonly proposed changes” on the Swift evolution web page, with a link to the discussion archives for it. While it shouldn’t be verboten to explore an area in the future, anyone doing so can reasonably be expected to read the previous discussions and only reopen it if there is new information or a new idea.

What do you think Doug?

I think it’s a good idea, which we can incorporate in the swift-evolution repository. My main concern is that I (personally), and the core team in general, probably won’t be able to proactively maintain such a list. We’d happily accept pull requests to help keep the list up-to-date and (as it grows longer) give it some organization.

You can replace the proposed statement `which` (another thread), the existing statement `?:` (this thread), and the global function `??` (which is an odd ball) with matching library methods.

A library method is likely slower than a built in at this stage until the optimiser improves, but a library function:

Is documented right in the IDE including code completion, statements aren’t (you don’t see quick help for `for`!)
Having a library function allows the use case to be throughly investigated. Is worth while as a language statement? What exact features are useful? EG should `which` support pattern matching, general boolean expressions, or simply be `Equatable` as shown below?
It is simpler to implement, maintain, and change a library function that a built-in.
There is no need for a keyword.

First `which`:

// Alternative to introducing `which` statement

final
class Which<I: Equatable, R> {
    private
    var result: R?
    
    private
    let which: I
    
    init(_ which: I) {
        self.which = which
    }
    
    func match(value: I, @noescape matchResult: () throws -> R) rethrows -> Self {
        if self.result == nil && self.which == value {
            self.result = try matchResult()
        }
        return self
    }
    
    func matchDefault(@noescape defaultResult: () throws -> R) rethrows -> R {
        switch self.result {
        case .None:
            return try defaultResult()
        case .Some(let value):
            return value
        }
    }
}

// Demo
enum Color {
    case Red, Blue, Green
}

// Which with a default value
let i1 = Which(Color.Red) // i = 16711680
    .match(.Red) { 0xFF0000 }
    .match(.Green) { 0x00FF00 }
    .match(.Blue) { 0x00000FF }
    .matchDefault { 0 }

// Which that throws an error if it defaults
let i2: Int! = Which(Color.Green) // i = 16711680
    .match(.Red) { 0xFF0000 }
    .match(.Green) { 0x00FF00 }
    .match(.Blue) { 0x00000FF }
    .matchDefault { nil } // Cant type call to fatalError as no return, hence nil and type Int! (note !)

Note runtime check for default rather than static check via compiler, not as good but not a big deal most of the time. The vast majority of languages don't do a compiler check on `switch`.

Similarly the `?:` statement can be replaced:

// Replacement for `?:` operator

struct IfFalse<R> {
    private
    let result: R?
    
    func ifFalse(@noescape falseResult: () throws -> R) rethrows -> R {
        switch self.result {
        case .None:
            return try falseResult()
        case .Some(let value):
            return value
        }
    }
}

extension Bool {
    func ifTrue<R>(@noescape trueResult: () throws -> R) rethrows -> IfFalse<R> {
        switch self {
        case true:
            return IfFalse(result: try trueResult())
        case false:
            return IfFalse(result: nil)
        }
    }
}

// Demo
let sB = true.ifTrue{"True"}.ifFalse{"False"} // "True" - for some reason needs {} and not () thinks () form throws

Whilst the `??` operator is already a library function it is difficult to see in an expression, it gets buried, and is inconsistent in style because it is a non-mathematical operator and a symbol rather than a keyword or keyword followed by a symbol. The space either side of the `??` operator also makes it look like both arguments are of equal importance, whereas it is the left hand side that is important and the right hand side is just a catch.

// Replacement for `??` operator

extension Optional {
    func ifNil(@noescape nilResult: () throws -> Wrapped) rethrows -> Wrapped {
        switch self {
        case .None:
            return try nilResult()
        case .Some(let value):
            return value
        }
    }
}

// Demo
let o: String? = nil
let sO = o.ifNil{"Nil"} // "Nil" - for some reason needs {} and not () thinks () form throws

···

Sent from my iPad

On 29 Dec 2015, at 4:00 AM, Thorsten Seitz via swift-evolution <swift-evolution@swift.org> wrote:

No exhaustiveness checking is a serious deficiency :-(

-Thorsten

Am 17.12.2015 um 08:09 schrieb Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org>:

Actually, this *almost* does what you want. No @autoclosure for the values and no exhaustiveness checking, but otherwise...

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

All,

I think, I finally might have the answer to improving ternary, with such a bold statement come some pretty high expectations but I think, I might actually have done it this time :-)

I am calling it the Demux Expression, it builds on the benefits of ternary and switch while improving on those.

This is a first draft, thanks in advance for feedback!

- Paul

What if you could wrap the existing switch statement in a closure and return a value from that closure like so

Let value = { switch (other) {
Case .Some(let value):
Return value // because this is in a closure the closure will return the value not the function this is in
Case .None:
Return "hello"
}}

···

Sent from my iPhone

On 29 Dec 2015, at 07:53, Howard Lovatt via swift-evolution <swift-evolution@swift.org> wrote:

You can replace the proposed statement `which` (another thread), the existing statement `?:` (this thread), and the global function `??` (which is an odd ball) with matching library methods.

A library method is likely slower than a built in at this stage until the optimiser improves, but a library function:

Is documented right in the IDE including code completion, statements aren’t (you don’t see quick help for `for`!)
Having a library function allows the use case to be throughly investigated. Is worth while as a language statement? What exact features are useful? EG should `which` support pattern matching, general boolean expressions, or simply be `Equatable` as shown below?
It is simpler to implement, maintain, and change a library function that a built-in.
There is no need for a keyword.

First `which`:

// Alternative to introducing `which` statement

final
class Which<I: Equatable, R> {
    private
    var result: R?
    
    private
    let which: I
    
    init(_ which: I) {
        self.which = which
    }
    
    func match(value: I, @noescape matchResult: () throws -> R) rethrows -> Self {
        if self.result == nil && self.which == value {
            self.result = try matchResult()
        }
        return self
    }
    
    func matchDefault(@noescape defaultResult: () throws -> R) rethrows -> R {
        switch self.result {
        case .None:
            return try defaultResult()
        case .Some(let value):
            return value
        }
    }
}

// Demo
enum Color {
    case Red, Blue, Green
}

// Which with a default value
let i1 = Which(Color.Red) // i = 16711680
    .match(.Red) { 0xFF0000 }
    .match(.Green) { 0x00FF00 }
    .match(.Blue) { 0x00000FF }
    .matchDefault { 0 }

// Which that throws an error if it defaults
let i2: Int! = Which(Color.Green) // i = 16711680
    .match(.Red) { 0xFF0000 }
    .match(.Green) { 0x00FF00 }
    .match(.Blue) { 0x00000FF }
    .matchDefault { nil } // Cant type call to fatalError as no return, hence nil and type Int! (note !)

Note runtime check for default rather than static check via compiler, not as good but not a big deal most of the time. The vast majority of languages don't do a compiler check on `switch`.

Similarly the `?:` statement can be replaced:

// Replacement for `?:` operator

struct IfFalse<R> {
    private
    let result: R?
    
    func ifFalse(@noescape falseResult: () throws -> R) rethrows -> R {
        switch self.result {
        case .None:
            return try falseResult()
        case .Some(let value):
            return value
        }
    }
}

extension Bool {
    func ifTrue<R>(@noescape trueResult: () throws -> R) rethrows -> IfFalse<R> {
        switch self {
        case true:
            return IfFalse(result: try trueResult())
        case false:
            return IfFalse(result: nil)
        }
    }
}

// Demo
let sB = true.ifTrue{"True"}.ifFalse{"False"} // "True" - for some reason needs {} and not () thinks () form throws

Whilst the `??` operator is already a library function it is difficult to see in an expression, it gets buried, and is inconsistent in style because it is a non-mathematical operator and a symbol rather than a keyword or keyword followed by a symbol. The space either side of the `??` operator also makes it look like both arguments are of equal importance, whereas it is the left hand side that is important and the right hand side is just a catch.

// Replacement for `??` operator

extension Optional {
    func ifNil(@noescape nilResult: () throws -> Wrapped) rethrows -> Wrapped {
        switch self {
        case .None:
            return try nilResult()
        case .Some(let value):
            return value
        }
    }
}

// Demo
let o: String? = nil
let sO = o.ifNil{"Nil"} // "Nil" - for some reason needs {} and not () thinks () form throws

Sent from my iPad

On 29 Dec 2015, at 4:00 AM, Thorsten Seitz via swift-evolution <swift-evolution@swift.org> wrote:

No exhaustiveness checking is a serious deficiency :-(

-Thorsten

Am 17.12.2015 um 08:09 schrieb Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org>:

Actually, this *almost* does what you want. No @autoclosure for the values and no exhaustiveness checking, but otherwise...

_______________________________________________
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

+1

I'd be very happy with your new proposal too. I still prefer sticking the
value we're using as a key outside of the parens, but it's a minor quibble.
Also I can't figure out if parens or curly braces are more appropriate.
Does it make more sense for the expression to look like a tuple or a
closure? I'm not sure.

Anyhow, I'm good with your new proposal.

It's a nice, consistent proposal, but I don't feel like this solves any of the complaints about the existing ternary operator:

- It's not obvious what it does when you first learn it.
- The '?' doesn't have anything to do with Optionals.

It is a way to put 'switch' into an expression. I'm not a fan of the two different colons, but that's "just" syntax.

Jordan

···

On Dec 18, 2015, at 14:04 , Paul Ossenbruggen via swift-evolution <swift-evolution@swift.org> wrote:

All,

I think, I finally might have the answer to improving ternary, with such a bold statement come some pretty high expectations but I think, I might actually have done it this time :-)

I am calling it the Demux Expression, it builds on the benefits of ternary and switch while improving on those.

https://github.com/possen/swift-evolution/blob/master/proposals/0024.md

This is a first draft, thanks in advance for feedback!

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

That looks pretty ugly.

I think the best we can hope for at this point is maybe another keyword that mirrors switch but is expression based (aka match) — leaving the ternary ? : expression as is - which is not all that bad since any if else that becomes a compound expression or more than two resultant values (chaining) quickly becomes a mess.

I am not sure that even a “match” expression would be accepted at this point because there seems to be general resistance to anything more than the existing paradigm with a few functional decorations — and the way of doing things is good enough.

Concurrency is also currently off the table at this point -- the fact that immutable pure functional code can theoretically be parsed into a dependance graph which would allow for out of order [within scope] parallel execution on different threads [not sure if the overhead of doing so would outweigh the benefits]…. would also not be of sufficient benefit.

The primary focus of Swift is a language for UI development, not server development….

···

On 2015-12-29, at 15:07:57, James Campbell via swift-evolution <swift-evolution@swift.org> wrote:

What if you could wrap the existing switch statement in a closure and return a value from that closure like so

Let value = { switch (other) {
Case .Some(let value):
Return value // because this is in a closure the closure will return the value not the function this is in
Case .None:
Return "hello"
}}

Sent from my iPhone

On 29 Dec 2015, at 07:53, Howard Lovatt via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

You can replace the proposed statement `which` (another thread), the existing statement `?:` (this thread), and the global function `??` (which is an odd ball) with matching library methods.

A library method is likely slower than a built in at this stage until the optimiser improves, but a library function:

Is documented right in the IDE including code completion, statements aren’t (you don’t see quick help for `for`!)
Having a library function allows the use case to be throughly investigated. Is worth while as a language statement? What exact features are useful? EG should `which` support pattern matching, general boolean expressions, or simply be `Equatable` as shown below?
It is simpler to implement, maintain, and change a library function that a built-in.
There is no need for a keyword.

First `which`:

// Alternative to introducing `which` statement

final
class Which<I: Equatable, R> {
    private
    var result: R?
    
    private
    let which: I
    
    init(_ which: I) {
        self.which = which
    }
    
    func match(value: I, @noescape matchResult: () throws -> R) rethrows -> Self {
        if self.result == nil && self.which == value {
            self.result = try matchResult()
        }
        return self
    }
    
    func matchDefault(@noescape defaultResult: () throws -> R) rethrows -> R {
        switch self.result {
        case .None:
            return try defaultResult()
        case .Some(let value):
            return value
        }
    }
}

// Demo
enum Color {
    case Red, Blue, Green
}

// Which with a default value
let i1 = Which(Color.Red) // i = 16711680 <tel:16711680>
    .match(.Red) { 0xFF0000 }
    .match(.Green) { 0x00FF00 }
    .match(.Blue) { 0x00000FF }
    .matchDefault { 0 }

// Which that throws an error if it defaults
let i2: Int! = Which(Color.Green) // i = 16711680 <tel:16711680>
    .match(.Red) { 0xFF0000 }
    .match(.Green) { 0x00FF00 }
    .match(.Blue) { 0x00000FF }
    .matchDefault { nil } // Cant type call to fatalError as no return, hence nil and type Int! (note !)

Note runtime check for default rather than static check via compiler, not as good but not a big deal most of the time. The vast majority of languages don't do a compiler check on `switch`.

Similarly the `?:` statement can be replaced:

// Replacement for `?:` operator

struct IfFalse<R> {
    private
    let result: R?
    
    func ifFalse(@noescape falseResult: () throws -> R) rethrows -> R {
        switch self.result {
        case .None:
            return try falseResult()
        case .Some(let value):
            return value
        }
    }
}

extension Bool {
    func ifTrue<R>(@noescape trueResult: () throws -> R) rethrows -> IfFalse<R> {
        switch self {
        case true:
            return IfFalse(result: try trueResult())
        case false:
            return IfFalse(result: nil)
        }
    }
}

// Demo
let sB = true.ifTrue{"True"}.ifFalse{"False"} // "True" - for some reason needs {} and not () thinks () form throws

Whilst the `??` operator is already a library function it is difficult to see in an expression, it gets buried, and is inconsistent in style because it is a non-mathematical operator and a symbol rather than a keyword or keyword followed by a symbol. The space either side of the `??` operator also makes it look like both arguments are of equal importance, whereas it is the left hand side that is important and the right hand side is just a catch.

// Replacement for `??` operator

extension Optional {
    func ifNil(@noescape nilResult: () throws -> Wrapped) rethrows -> Wrapped {
        switch self {
        case .None:
            return try nilResult()
        case .Some(let value):
            return value
        }
    }
}

// Demo
let o: String? = nil
let sO = o.ifNil{"Nil"} // "Nil" - for some reason needs {} and not () thinks () form throws

Sent from my iPad

On 29 Dec 2015, at 4:00 AM, Thorsten Seitz via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

No exhaustiveness checking is a serious deficiency :-(

-Thorsten

Am 17.12.2015 um 08:09 schrieb Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

Actually, this *almost* does what you want. No @autoclosure for the values and no exhaustiveness checking, but otherwise...

_______________________________________________
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

Thanks for your support!

I think parenthesis are preferred because braces are for bracketing lists of statements. Statements may or may not return values.

Having the conditional on the inside of the parens helps to show the begging of the demux operator rather than a floating conditional which is a common complaint with the ternary operator.

···

On Dec 18, 2015, at 3:46 PM, Charles Constant <charles@charlesism.com> wrote:

+1

I'd be very happy with your new proposal too. I still prefer sticking the value we're using as a key outside of the parens, but it's a minor quibble. Also I can't figure out if parens or curly braces are more appropriate. Does it make more sense for the expression to look like a tuple or a closure? I'm not sure.

Anyhow, I'm good with your new proposal.

It's a nice, consistent proposal, but I don't feel like this solves any of the complaints about the existing ternary operator:

- It's not obvious what it does when you first learn it.
- The '?' doesn't have anything to do with Optionals.

It is a way to put 'switch' into an expression. I'm not a fan of the two different colons, but that's "just" syntax.

+1 to all that

Jordan

All,

I think, I finally might have the answer to improving ternary, with such a bold statement come some pretty high expectations but I think, I might actually have done it this time :-)

I am calling it the Demux Expression, it builds on the benefits of ternary and switch while improving on those.

https://github.com/possen/swift-evolution/blob/master/proposals/0024.md

This is a first draft, thanks in advance for feedback!

- Paul
_______________________________________________
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

-Dave

···

On Dec 19, 2015, at 7:55 PM, Jordan Rose via swift-evolution <swift-evolution@swift.org> wrote:

On Dec 18, 2015, at 14:04 , Paul Ossenbruggen via swift-evolution <swift-evolution@swift.org> wrote:

Thank you for your feedback!

Just want to respond a little bit, I don’t believe it is any worse than the ternary in terms of it being non obvious as to what it does, and it adds a lot of power beyond the ternary, because now you can switch to more than two outcomes.

In terms of readability, it does improve on the ternary because I often find it hard to determine where a ternary begins, in unfamiliar code, looking at:

x = x == y ? 49 : 3

It is not until you get to the question mark that you realize it is a ternary and the x == y just is kind of floating there. The = and == just looks strange. Many do this to make it look better:

x = (x == y ? 49 : 3)

With the new form:

x = ?(x == y : 49, 3)

The x == y pops out more, similar to how adding the parenthesis helps, and you can immediately tell that this is part of a demux expression. This codifies the practice, of putting in parenthesis, in the same way that mandatory braces in control flow statements make the statements stand out. So I do believe it does address some of the problems with ternary.

If that is not enough of a difference from ternary to make this proposal fly, then maybe the hybrid approach outlined in Alternatives Considered section would be preferred? One example:

x = if(x == y : 49, 3)

This looks more function like and has many of the benefits of my original proposal and perhaps better addresses some of the downside of the ? being non obvious as to what it does at the expense of conciseness. Although, as Austin Zheng says, ? is for query, not necessarily just to indicate optional. If we don’t go with that interpretation of ?, this direction, may help to keep the concept of ? for optionals. Ternary kind of steps into that and makes it less separated so this alternative may be a better direction. I don’t object to this approach, if that is the consensus.

- Paul

···

On Dec 19, 2015, at 7:55 PM, Jordan Rose <jordan_rose@apple.com> wrote:

It's a nice, consistent proposal, but I don't feel like this solves any of the complaints about the existing ternary operator:

- It's not obvious what it does when you first learn it.
- The '?' doesn't have anything to do with Optionals.

It is a way to put 'switch' into an expression. I'm not a fan of the two different colons, but that's "just" syntax.

Jordan

On Dec 18, 2015, at 14:04 , Paul Ossenbruggen via swift-evolution <swift-evolution@swift.org> wrote:

All,

I think, I finally might have the answer to improving ternary, with such a bold statement come some pretty high expectations but I think, I might actually have done it this time :-)

I am calling it the Demux Expression, it builds on the benefits of ternary and switch while improving on those.

https://github.com/possen/swift-evolution/blob/master/proposals/0024.md

This is a first draft, thanks in advance for feedback!

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

That looks pretty ugly.

I think the best we can hope for at this point is maybe another keyword that mirrors switch but is expression based (aka match) — leaving the ternary ? : expression as is - which is not all that bad since any if else that becomes a compound expression or more than two resultant values (chaining) quickly becomes a mess.

I agree that this is probably the best path forward at the moment. There was a post early on showing a ternary-like switch expression. I don't remember whether there were any specific problems with that idea or not, but if there aren't that might best route forward.

···

Sent from my iPad

On Dec 29, 2015, at 7:28 AM, Craig Cruden via swift-evolution <swift-evolution@swift.org> wrote:

I am not sure that even a “match” expression would be accepted at this point because there seems to be general resistance to anything more than the existing paradigm with a few functional decorations — and the way of doing things is good enough.

Concurrency is also currently off the table at this point -- the fact that immutable pure functional code can theoretically be parsed into a dependance graph which would allow for out of order [within scope] parallel execution on different threads [not sure if the overhead of doing so would outweigh the benefits]…. would also not be of sufficient benefit.

The primary focus of Swift is a language for UI development, not server development….

On 2015-12-29, at 15:07:57, James Campbell via swift-evolution <swift-evolution@swift.org> wrote:

What if you could wrap the existing switch statement in a closure and return a value from that closure like so

Let value = { switch (other) {
Case .Some(let value):
Return value // because this is in a closure the closure will return the value not the function this is in
Case .None:
Return "hello"
}}

Sent from my iPhone

On 29 Dec 2015, at 07:53, Howard Lovatt via swift-evolution <swift-evolution@swift.org> wrote:

You can replace the proposed statement `which` (another thread), the existing statement `?:` (this thread), and the global function `??` (which is an odd ball) with matching library methods.

A library method is likely slower than a built in at this stage until the optimiser improves, but a library function:

Is documented right in the IDE including code completion, statements aren’t (you don’t see quick help for `for`!)
Having a library function allows the use case to be throughly investigated. Is worth while as a language statement? What exact features are useful? EG should `which` support pattern matching, general boolean expressions, or simply be `Equatable` as shown below?
It is simpler to implement, maintain, and change a library function that a built-in.
There is no need for a keyword.

First `which`:

// Alternative to introducing `which` statement

final
class Which<I: Equatable, R> {
    private
    var result: R?
    
    private
    let which: I
    
    init(_ which: I) {
        self.which = which
    }
    
    func match(value: I, @noescape matchResult: () throws -> R) rethrows -> Self {
        if self.result == nil && self.which == value {
            self.result = try matchResult()
        }
        return self
    }
    
    func matchDefault(@noescape defaultResult: () throws -> R) rethrows -> R {
        switch self.result {
        case .None:
            return try defaultResult()
        case .Some(let value):
            return value
        }
    }
}

// Demo
enum Color {
    case Red, Blue, Green
}

// Which with a default value
let i1 = Which(Color.Red) // i = 16711680
    .match(.Red) { 0xFF0000 }
    .match(.Green) { 0x00FF00 }
    .match(.Blue) { 0x00000FF }
    .matchDefault { 0 }

// Which that throws an error if it defaults
let i2: Int! = Which(Color.Green) // i = 16711680
    .match(.Red) { 0xFF0000 }
    .match(.Green) { 0x00FF00 }
    .match(.Blue) { 0x00000FF }
    .matchDefault { nil } // Cant type call to fatalError as no return, hence nil and type Int! (note !)

Note runtime check for default rather than static check via compiler, not as good but not a big deal most of the time. The vast majority of languages don't do a compiler check on `switch`.

Similarly the `?:` statement can be replaced:

// Replacement for `?:` operator

struct IfFalse<R> {
    private
    let result: R?
    
    func ifFalse(@noescape falseResult: () throws -> R) rethrows -> R {
        switch self.result {
        case .None:
            return try falseResult()
        case .Some(let value):
            return value
        }
    }
}

extension Bool {
    func ifTrue<R>(@noescape trueResult: () throws -> R) rethrows -> IfFalse<R> {
        switch self {
        case true:
            return IfFalse(result: try trueResult())
        case false:
            return IfFalse(result: nil)
        }
    }
}

// Demo
let sB = true.ifTrue{"True"}.ifFalse{"False"} // "True" - for some reason needs {} and not () thinks () form throws

Whilst the `??` operator is already a library function it is difficult to see in an expression, it gets buried, and is inconsistent in style because it is a non-mathematical operator and a symbol rather than a keyword or keyword followed by a symbol. The space either side of the `??` operator also makes it look like both arguments are of equal importance, whereas it is the left hand side that is important and the right hand side is just a catch.

// Replacement for `??` operator

extension Optional {
    func ifNil(@noescape nilResult: () throws -> Wrapped) rethrows -> Wrapped {
        switch self {
        case .None:
            return try nilResult()
        case .Some(let value):
            return value
        }
    }
}

// Demo
let o: String? = nil
let sO = o.ifNil{"Nil"} // "Nil" - for some reason needs {} and not () thinks () form throws

Sent from my iPad

On 29 Dec 2015, at 4:00 AM, Thorsten Seitz via swift-evolution <swift-evolution@swift.org> wrote:

No exhaustiveness checking is a serious deficiency :-(

-Thorsten

Am 17.12.2015 um 08:09 schrieb Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org>:

Actually, this *almost* does what you want. No @autoclosure for the values and no exhaustiveness checking, but otherwise...

_______________________________________________
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

Hey, I really like the new proposal, gets a +1from me! Especially the case part makes it good to use. I would just leave out the Integer part because it can be confusing and misleading. Do you start counting at 1? Or at 0? MinInt? It’s just super error-prone if you don’t write the number before it.

?(charNum : 0: "A", 1: "B”, ...
might be OK as it would get rid of the many case statements but then again, is this only working for Int or also for float?

···

On 19 Dec 2015, at 01:56, Paul Ossenbruggen via swift-evolution <swift-evolution@swift.org> wrote:

Thanks for your support!

I think parenthesis are preferred because braces are for bracketing lists of statements. Statements may or may not return values.

Having the conditional on the inside of the parens helps to show the begging of the demux operator rather than a floating conditional which is a common complaint with the ternary operator.

On Dec 18, 2015, at 3:46 PM, Charles Constant <charles@charlesism.com> wrote:

+1

I'd be very happy with your new proposal too. I still prefer sticking the value we're using as a key outside of the parens, but it's a minor quibble. Also I can't figure out if parens or curly braces are more appropriate. Does it make more sense for the expression to look like a tuple or a closure? I'm not sure.

Anyhow, I'm good with your new proposal.

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

+1 to Jordan's points as well.

Generally speaking, there is clearly a wide variety of things that cause
people to be interested in this particular proposal and I don't think we
can reconcile all of them. For example, I think that "collapsing an if
statement into one line" isn't a good enough reason to introduce the
clutter and potential for abuse of the original ternary syntax into a
codebase, so I generally float the idea to ban it from projects I'm
involved in as soon as I see it pop up in one. At the same time, there seem
to be people who are enamored with the concept, and maybe instead they talk
in this thread because they want a way to condense a switch statement into
one line. And still others think that there is no rush to think about
getting rid of ternary, unless we come up with something equally concise or
with significant advantages to warrant removing it (all valid points).

I'm not *against* Paul's idea, but if it matters at all (i.e. if you are
worried other people will think like me), if this syntax is released, I
will most likely float the idea of opting out of it immediately to my
project collaborators.

While interesting for quick, proof of concept coding sessions, it already
has some of the readability and abusability disadvantages already present
in ternary, and it's still just in the proposal stage.

I only hope that this doesn't preclude progress on turning fully-qualified
(and indented) statements into expressions.

···

On Sat, Dec 19, 2015 at 10:57 PM Dave Abrahams via swift-evolution < swift-evolution@swift.org> wrote:

> On Dec 19, 2015, at 7:55 PM, Jordan Rose via swift-evolution < > swift-evolution@swift.org> wrote:
>
> It's a nice, consistent proposal, but I don't feel like this solves any
of the complaints about the existing ternary operator:
>
> - It's not obvious what it does when you first learn it.
> - The '?' doesn't have anything to do with Optionals.
>
> It is a way to put 'switch' into an expression. I'm not a fan of the two
different colons, but that's "just" syntax.

+1 to all that

> Jordan
>
>> On Dec 18, 2015, at 14:04 , Paul Ossenbruggen via swift-evolution < > swift-evolution@swift.org> wrote:
>>
>> All,
>>
>> I think, I finally might have the answer to improving ternary, with
such a bold statement come some pretty high expectations but I think, I
might actually have done it this time :-)
>>
>> I am calling it the Demux Expression, it builds on the benefits of
ternary and switch while improving on those.
>>
>> https://github.com/possen/swift-evolution/blob/master/proposals/0024.md
>>
>> This is a first draft, thanks in advance for feedback!
>>
>> - Paul
>> _______________________________________________
>> 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

-Dave

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

I'm with Matthew/Craig.

We discussed a couple very ternary-like versions earlier in the thread,
which I increasingly think are the best options.

The major objection to this came from Lattner, and his objection, if I have
it right, is "this proposal doesn't add enough functionality to justify the
additional complexity/confusion"

The sticking point, at the moment, is formulating a really persuasive
argument for "why we need this." If we can't do that, this proposal is dead.

···

On Tue, Dec 29, 2015 at 5:38 AM, Matthew Johnson via swift-evolution < swift-evolution@swift.org> wrote:

Sent from my iPad

On Dec 29, 2015, at 7:28 AM, Craig Cruden via swift-evolution < > swift-evolution@swift.org> wrote:

That looks pretty ugly.

I think the best we can hope for at this point is maybe another keyword
that mirrors switch but is expression based (aka match) — leaving the
ternary ? : expression as is - which is not all that bad since any if else
that becomes a compound expression or more than two resultant values
(chaining) quickly becomes a mess.

I agree that this is probably the best path forward at the moment. There
was a post early on showing a ternary-like switch expression. I don't
remember whether there were any specific problems with that idea or not,
but if there aren't that might best route forward.

I am not sure that even a “match” expression would be accepted at this
point because there seems to be general resistance to anything more than
the existing paradigm with a few functional decorations — and the way of
doing things is good enough.

Concurrency is also currently off the table at this point -- the fact that
immutable pure functional code can theoretically be parsed into a
dependance graph which would allow for out of order [within scope] parallel
execution on different threads [not sure if the overhead of doing so would
outweigh the benefits]…. would also not be of sufficient benefit.

The primary focus of Swift is a language for UI development, not server
development….

On 2015-12-29, at 15:07:57, James Campbell via swift-evolution < > swift-evolution@swift.org> wrote:

What if you could wrap the existing switch statement in a closure and
return a value from that closure like so

Let value = { switch (other) {
Case .Some(let value):
Return value // because this is in a closure the closure will return the
value not the function this is in
Case .None:
Return "hello"
}}

Sent from my iPhone

On 29 Dec 2015, at 07:53, Howard Lovatt via swift-evolution < > swift-evolution@swift.org> wrote:

You can replace the proposed statement `which` (another thread), the
existing statement `?:` (this thread), and the global function `??` (which
is an odd ball) with matching library methods.

A library method is likely slower than a built in at this stage until the
optimiser improves, but a library function:

   1. Is documented right in the IDE including code completion,
   statements aren’t (you don’t see quick help for `for`!)
   2. Having a library function allows the use case to be throughly
   investigated. Is worth while as a language statement? What exact features
   are useful? EG should `which` support pattern matching, general boolean
   expressions, or simply be `Equatable` as shown below?
   3. It is simpler to implement, maintain, and change a library function
   that a built-in.
   4. There is no need for a keyword.

First `which`:

// Alternative to introducing `which` statement

final
class Which<I: Equatable, R> {
    private
    var result: R?

    private
    let which: I

    init(_ which: I) {
        self.which = which
    }

    func match(value: I, @noescape matchResult: () throws -> R) rethrows
-> Self {
        if self.result == nil && self.which == value {
            self.result = try matchResult()
        }
        return self
    }

    func matchDefault(@noescape defaultResult: () throws -> R) rethrows
-> R {
        switch self.result {
        case .None:
            return try defaultResult()
        case .Some(let value):
            return value
        }
    }
}

// Demo
enum Color {
    case Red, Blue, Green
}

// Which with a default value
let i1 = Which(Color.Red) // i = 16711680
    .match(.Red) { 0xFF0000 }
    .match(.Green) { 0x00FF00 }
    .match(.Blue) { 0x00000FF }
    .matchDefault { 0 }

// Which that throws an error if it defaults
let i2: Int! = Which(Color.Green) // i = 16711680
    .match(.Red) { 0xFF0000 }
    .match(.Green) { 0x00FF00 }
    .match(.Blue) { 0x00000FF }
    .matchDefault { nil } // Cant type call to fatalError as no return,
hence nil and type Int! (note !)

Note runtime check for default rather than static check via compiler, not
as good but not a big deal most of the time. The vast majority of languages
don't do a compiler check on `switch`.

Similarly the `?:` statement can be replaced:

// Replacement for `?:` operator

struct IfFalse<R> {
    private
    let result: R?

    func ifFalse(@noescape falseResult: () throws -> R) rethrows -> R {
        switch self.result {
        case .None:
            return try falseResult()
        case .Some(let value):
            return value
        }
    }
}

extension Bool {
    func ifTrue<R>(@noescape trueResult: () throws -> R) rethrows ->
IfFalse<R> {
        switch self {
        case true:
            return IfFalse(result: try trueResult())
        case false:
            return IfFalse(result: nil)
        }
    }
}

// Demo
let sB = true.ifTrue{"True"}.ifFalse{"False"} // "True" - for some reason
needs {} and not () thinks () form throws

Whilst the `??` operator is already a library function it is difficult to
see in an expression, it gets buried, and is inconsistent in style because
it is a non-mathematical operator and a symbol rather than a keyword or
keyword followed by a symbol. The space either side of the `??` operator
also makes it look like both arguments are of equal importance, whereas it
is the left hand side that is important and the right hand side is just a
catch.

// Replacement for `??` operator

extension Optional {
    func ifNil(@noescape nilResult: () throws -> Wrapped) rethrows ->
Wrapped {
        switch self {
        case .None:
            return try nilResult()
        case .Some(let value):
            return value
        }
    }
}

// Demo
let o: String? = nil
let sO = o.ifNil{"Nil"} // "Nil" - for some reason needs {} and not ()
thinks () form throws

Sent from my iPad

On 29 Dec 2015, at 4:00 AM, Thorsten Seitz via swift-evolution < > swift-evolution@swift.org> wrote:

No exhaustiveness checking is a serious deficiency :-(

-Thorsten

Am 17.12.2015 um 08:09 schrieb Brent Royal-Gordon via swift-evolution < > swift-evolution@swift.org>:

Actually, this *almost* does what you want. No @autoclosure for the values
and no exhaustiveness checking, but otherwise...

_______________________________________________
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

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