ternary operator ?: suggestion

(I originally posted as possen p and since hopefully fixed that)

So, As Kevin Ballard and Ole Bergman pointed out, making a regular “if” an expression has some major complications. Having the potential to return different object types in a strictly typed language will not be easy. Also what to do if the if does not have an else. The ternary operator does not allow the if/else results of different types. In Python doing an expression with if/else is not a problem because it will let you return different types, this is legal, (which makes sense for Python):

val = 10 if p == 5 else “abc"

it will not however let you omit the else part:

val = 10 if False // produces an error.

so it appears if you assign to an expression in Python it will require the else part. So it is different than a regular if.

So I ask, is making the regular “if" an expression truly desired for Swift? I do see other languages doing it, but it does seem a bit odd to assign from any “if”. In my original proposal I was suggesting that assignment is done like this:

let val = 10 else 40 if p == 5

In this it is very similar to the ternary expression in that else part is required and both types must match, and has improved readability. This is overloading the if/else keywords to provide essentially the same thing as ternary operators..

The following from Kevin Ballard's email:

let foo = if condition {
    funcWithSideEffectsThatReturnsInt()
} else {
    funcWithSideEffectsThatReturnsString()
}

I find it is hard to tell that the two results are returning a value. To me it looks like it is either ignoring the return value or is returning void.

Maybe emphasizing the assignment would help as below does not have return values in the method names:

let foo if condition {
    = dosomething()
} else {
    = dosomethingelse()
}

putting the = after the else shows that it is returning a value for the let clause. It also helps emphasize that the return types must match and I think looks better.

Going back to my proposal with the = suggestion:

let foo = dosomething() else = dosomethingelse() if condition

this really shows that the let variable is being assigned.

Note that these are still chainable expressions:

var foo = ((10 else = 40 if p == 5) else = (15 else = 14 if p == 10) if p == 4)

It may be good to make the space optional after the else:

var foo = ((10 else= 40 if p == 5) else= (15 else= 14 if p == 10) if p == 4)

- Paul

Wouldn’t it be possible to still have if-statements?

When used as expression an if-expression would of course require an else-part, but when used as statement we would still have an if-statement (not an if-expression) and the else-part would be optional just like now.

-Thorsten

···

Am 06.12.2015 um 00:12 schrieb Ole Begemann via swift-evolution <swift-evolution@swift.org>:

This is subtle and requires thought

What do you envision as the pitfalls of the design thinking behind if/switch expressions? I’m not a compiler programmer, but a prog-lang enthusiast. I wouldn’t mind a little nudge in the right direction.

One thing that comes to mind is that if "if" is an expression, every if needs an else branch. This makes it harder to use if to conditionally perform side effects. For example, this is currently valid:

func doSomething() { ... }

if condition {
   doSomething()
}

Should this be allowed? You could argue that this should work because (a) the expression result is unused and (b) the return type of doSomething is Void, so the compiler could conceivably implicitly construct an else branch that returns (). But it would be inconsistent with other expressions. Disallowing this style could make writing typical "imperative" code harder.

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

I’d prefer if-expressions to have the same form as if-statements, i.e.

let greeting = if let name = optionalName { "Hello, \(name),“ } else { “To Whom It May Concern:” }

-Thorsten

PS: As an aside this could also be written without an if-expression as follows:

let greeting = name.map(„Hello, \($0),“) ?? “To Whom It May Concern:”

With regards to the question about the required else part:

···

Am 06.12.2015 um 01:12 schrieb Alex Lew via swift-evolution <swift-evolution@swift.org>:

let greeting = “Hello, \(name),” if let name = optionalName else “To Whom It May Concern:”

I don't think you can just get rid of the if statement in favor of an
expression. You still want to be able to do this:

if (condition) {
    funcWithSideEffectsThatReturnsInt()
} else {
    funcWithSideEffectsThatReturnsString()
}

but that's not a valid expression (what is its type?).

···

On Sat, Dec 5, 2015 at 7:24 PM, Ole Begemann via swift-evolution < swift-evolution@swift.org> wrote:

Yeah, returning an optional is a good idea.

> On 06 Dec 2015, at 01:22, Adam C. Lickel <adam@lickel.com> wrote:
>
> In that situation, the if statement could return a Void? where the else
implicitly returns nil
>
>> On Dec 5, 2015, at 3:12 PM, Ole Begemann via swift-evolution < > swift-evolution@swift.org> wrote:
>>
>>>> This is subtle and requires thought
>>>
>>> What do you envision as the pitfalls of the design thinking behind
if/switch expressions? I’m not a compiler programmer, but a prog-lang
enthusiast. I wouldn’t mind a little nudge in the right direction.
>>
>> One thing that comes to mind is that if "if" is an expression, every if
needs an else branch. This makes it harder to use if to conditionally
perform side effects. For example, this is currently valid:
>>
>> func doSomething() { ... }
>>
>> if condition {
>> doSomething()
>> }
>>
>> Should this be allowed? You could argue that this should work because
(a) the expression result is unused and (b) the return type of doSomething
is Void, so the compiler could conceivably implicitly construct an else
branch that returns (). But it would be inconsistent with other
expressions. Disallowing this style could make writing typical "imperative"
code harder.
>>
>> – Ole
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I don't think you can just get rid of the if statement in favor of an expression. You still want to be able to do this:

if (condition) {
    funcWithSideEffectsThatReturnsInt()
} else {
    funcWithSideEffectsThatReturnsString()
}

but that's not a valid expression (what is its type?).

An if statement with two different types could just have the closes common ancestor or Any as type.

Yeah, returning an optional is a good idea.

This would, interestingly, make the else statement very similar to the optional-chaining operator ?? with the small difference that the second argument is a block (and does ?? autoclosure the second arg?).

Another idea is put the conditional at the beginning:

let foo if conditional then = 10 else = 20

But that requires an additional keyword to separate out the conditional but might read better.

···

On Dec 6, 2015, at 2:14 AM, possen p <possen@gmail.com> wrote:

(I originally posted as possen p and since hopefully fixed that)

So, As Kevin Ballard and Ole Bergman pointed out, making a regular “if” an expression has some major complications. Having the potential to return different object types in a strictly typed language will not be easy. Also what to do if the if does not have an else. The ternary operator does not allow the if/else results of different types. In Python doing an expression with if/else is not a problem because it will let you return different types, this is legal, (which makes sense for Python):

val = 10 if p == 5 else “abc"

it will not however let you omit the else part:

val = 10 if False // produces an error.

so it appears if you assign to an expression in Python it will require the else part. So it is different than a regular if.

So I ask, is making the regular “if" an expression truly desired for Swift? I do see other languages doing it, but it does seem a bit odd to assign from any “if”. In my original proposal I was suggesting that assignment is done like this:

let val = 10 else 40 if p == 5

In this it is very similar to the ternary expression in that else part is required and both types must match, and has improved readability. This is overloading the if/else keywords to provide essentially the same thing as ternary operators..

The following from Kevin Ballard's email:

let foo = if condition {
    funcWithSideEffectsThatReturnsInt()
} else {
    funcWithSideEffectsThatReturnsString()
}

I find it is hard to tell that the two results are returning a value. To me it looks like it is either ignoring the return value or is returning void.

Maybe emphasizing the assignment would help as below does not have return values in the method names:

let foo if condition {
    = dosomething()
} else {
    = dosomethingelse()
}

putting the = after the else shows that it is returning a value for the let clause. It also helps emphasize that the return types must match and I think looks better.

Going back to my proposal with the = suggestion:

let foo = dosomething() else = dosomethingelse() if condition

this really shows that the let variable is being assigned.

Note that these are still chainable expressions:

var foo = ((10 else = 40 if p == 5) else = (15 else = 14 if p == 10) if p == 4)

It may be good to make the space optional after the else:

var foo = ((10 else= 40 if p == 5) else= (15 else= 14 if p == 10) if p == 4)

- Paul

That would actually be no problem if Swift’s type system would have union types (Ceylon has union and intersection types which are quite awesome and enable lots of nice things quite naturally, see http://ceylon-lang.org/documentation/1.2/tour/types/\).

In that case the type of such an expression would just be the union of both types, which is written Int | String in Ceylon.

-Thorsten

···

Am 06.12.2015 um 01:28 schrieb Alex Lew via swift-evolution <swift-evolution@swift.org>:

I don't think you can just get rid of the if statement in favor of an expression. You still want to be able to do this:

if (condition) {
    funcWithSideEffectsThatReturnsInt()
} else {
    funcWithSideEffectsThatReturnsString()
}

but that's not a valid expression (what is its type?).

That's a great way to cause confusion.

Rust has this feature (all statements are expressions), and it requires
if statements to have an else branch with the same type unless the type
is `()`. It's solution to the issue of the branches returning unwanted
values is that Rust uses semicolons, and the semicolon acts sort of like
an operator that consumes any value and returns `()`, so if you
terminate the last statement of the branch with a semicolon, the whole
branch returns `()`, and if you leave it off, the branch returns a
value. It's actually very elegant and straightforward.

That said, proposing that Swift introduce this same rule for semicolons
is probably not a good idea. We certainly could declare that an explicit
semicolon has this behavior, so you'd see people writing code like

if condition { funcWithSideEffectsThatReturnsInt(); }

but it would be confusing because semicolons are almost never
used in Swift.

An alternative that would work today is just relying on assignment
returning Void, so you can write

if condition { _ = funcWithSideEffectsThatReturnsInt() }

but that looks kind of weird and would probably also be confusing.
Better than the semicolon rule I think, but still not great.

Another option is to check if the return value is actually used
anywhere, and if it's not, then silently coerce it to Void. This way
you can write

if condition { funcWithSideEffectsThatReturnsInt() } else {
funcWithSideEffectsThatReturnsString() }

and it would be fine but writing

let foo = if condition { funcWithSideEffectsThatReturnsInt() } else {
funcWithSideEffectsThatReturnsString() }

would fail with a type error.

I suspect that this is the right approach, but it does involve a
bit of magic.

-Kevin

···

On Sat, Dec 5, 2015, at 04:32 PM, Lukas Stabe via swift-evolution wrote:

I don't think you can just get rid of the if statement in favor of an
expression. You still want to be able to do this:

if (condition) { funcWithSideEffectsThatReturnsInt() } else {
funcWithSideEffectsThatReturnsString() }

but that's not a valid expression (what is its type?).

An if statement with two different types could just have the closes
common ancestor or Any as type.

That approach seems fine to me; I don't think it seems like magic.

"if x { returnsAnInt() } else { returnsAString() }" would have type Any,
but would only emit a warning if you actually tried to *use* the value.
Much like the current warning, "*X inferred to have type Any, which may be
unexpected*".

Jacob Bandes-Storch

···

On Sat, Dec 5, 2015 at 7:53 PM, Kevin Ballard via swift-evolution < swift-evolution@swift.org> wrote:

On Sat, Dec 5, 2015, at 04:32 PM, Lukas Stabe via swift-evolution wrote:

I don't think you can just get rid of the if statement in favor of an
expression. You still want to be able to do this:

if (condition) {
    funcWithSideEffectsThatReturnsInt()
} else {
    funcWithSideEffectsThatReturnsString()
}

but that's not a valid expression (what is its type?).

An if statement with two different types could just have the closes common
ancestor or Any as type.

That's a great way to cause confusion.

Rust has this feature (all statements are expressions), and it requires if
statements to have an else branch with the same type unless the type is
`()`. It's solution to the issue of the branches returning unwanted values
is that Rust uses semicolons, and the semicolon acts sort of like an
operator that consumes any value and returns `()`, so if you terminate the
last statement of the branch with a semicolon, the whole branch returns
`()`, and if you leave it off, the branch returns a value. It's actually
very elegant and straightforward.

That said, proposing that Swift introduce this same rule for semicolons is
probably not a good idea. We certainly could declare that an explicit
semicolon has this behavior, so you'd see people writing code like

if condition {
    funcWithSideEffectsThatReturnsInt();
}

but it would be confusing because semicolons are almost never used in
Swift.

An alternative that would work today is just relying on assignment
returning Void, so you can write

if condition {
    _ = funcWithSideEffectsThatReturnsInt()
}

but that looks kind of weird and would probably also be confusing. Better
than the semicolon rule I think, but still not great.

Another option is to check if the return value is actually used anywhere,
and if it's not, then silently coerce it to Void. This way you can write

if condition {
    funcWithSideEffectsThatReturnsInt()
} else {
    funcWithSideEffectsThatReturnsString()
}

and it would be fine but writing

let foo = if condition {
    funcWithSideEffectsThatReturnsInt()
} else {
    funcWithSideEffectsThatReturnsString()
}

would fail with a type error.

I suspect that this is the right approach, but it does involve a bit of
magic.

-Kevin

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

If it's defined as type Any, then we'd also need a guaranteed compiler
optimization (preferably one that happens in debug builds too) that
omits the creation of the `Any` value if it's unused.

-Kevin

That approach seems fine to me; I don't think it seems like magic.

"if x { returnsAnInt() } else { returnsAString() }" would have type
Any, but would only emit a warning if you actually tried to *use* the
value. Much like the current warning, "*X inferred to have type Any,
which may be unexpected*".

Jacob Bandes-Storch

__

I don't think you can just get rid of the if statement in favor of
an expression. You still want to be able to do this:

if (condition) { funcWithSideEffectsThatReturnsInt() } else {
funcWithSideEffectsThatReturnsString() }

but that's not a valid expression (what is its type?).

An if statement with two different types could just have the closes
common ancestor or Any as type.

That's a great way to cause confusion.

Rust has this feature (all statements are expressions), and it
requires if statements to have an else branch with the same type
unless the type is `()`. It's solution to the issue of the branches
returning unwanted values is that Rust uses semicolons, and the
semicolon acts sort of like an operator that consumes any value and
returns `()`, so if you terminate the last statement of the branch
with a semicolon, the whole branch returns `()`, and if you leave it
off, the branch returns a value. It's actually very elegant and
straightforward.

That said, proposing that Swift introduce this same rule for
semicolons is probably not a good idea. We certainly could declare
that an explicit semicolon has this behavior, so you'd see people
writing code like

if condition { funcWithSideEffectsThatReturnsInt(); }

but it would be confusing because semicolons are almost never used
in Swift.

An alternative that would work today is just relying on assignment
returning Void, so you can write

if condition { _ = funcWithSideEffectsThatReturnsInt() }

but that looks kind of weird and would probably also be confusing.
Better than the semicolon rule I think, but still not great.

Another option is to check if the return value is actually used
anywhere, and if it's not, then silently coerce it to Void. This way
you can write

if condition { funcWithSideEffectsThatReturnsInt() } else {
funcWithSideEffectsThatReturnsString() }

and it would be fine but writing

let foo = if condition { funcWithSideEffectsThatReturnsInt() }
else { funcWithSideEffectsThatReturnsString() }

would fail with a type error.

I suspect that this is the right approach, but it does involve a bit
of magic.

-Kevin

_______________________________________________

swift-evolution mailing list

···

On Sat, Dec 5, 2015, at 08:46 PM, Jacob Bandes-Storch wrote:

On Sat, Dec 5, 2015 at 7:53 PM, Kevin Ballard via swift-evolution <swift- > evolution@swift.org> wrote:

On Sat, Dec 5, 2015, at 04:32 PM, Lukas Stabe via swift- >> evolution wrote:
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I agree.

I’m sure there are use-cases where the different branches return different types, both conforming to some protocol. Since we’d need to support these cases, this approach is also pretty clean, because Any is just another protocol.

The warning emitted should be silenceable by explicitly declaring the variable being assigned to as Any.

Lukas

···

On 06 Dec 2015, at 05:46, Jacob Bandes-Storch via swift-evolution <swift-evolution@swift.org> wrote:

That approach seems fine to me; I don't think it seems like magic.

"if x { returnsAnInt() } else { returnsAString() }" would have type Any, but would only emit a warning if you actually tried to use the value. Much like the current warning, "X inferred to have type Any, which may be unexpected".

Jacob Bandes-Storch

On Sat, Dec 5, 2015 at 7:53 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
On Sat, Dec 5, 2015, at 04:32 PM, Lukas Stabe via swift-evolution wrote:

I don't think you can just get rid of the if statement in favor of an expression. You still want to be able to do this:

if (condition) {
    funcWithSideEffectsThatReturnsInt()
} else {
    funcWithSideEffectsThatReturnsString()
}

but that's not a valid expression (what is its type?).

An if statement with two different types could just have the closes common ancestor or Any as type.

That's a great way to cause confusion.

Rust has this feature (all statements are expressions), and it requires if statements to have an else branch with the same type unless the type is `()`. It's solution to the issue of the branches returning unwanted values is that Rust uses semicolons, and the semicolon acts sort of like an operator that consumes any value and returns `()`, so if you terminate the last statement of the branch with a semicolon, the whole branch returns `()`, and if you leave it off, the branch returns a value. It's actually very elegant and straightforward.

That said, proposing that Swift introduce this same rule for semicolons is probably not a good idea. We certainly could declare that an explicit semicolon has this behavior, so you'd see people writing code like

if condition {
    funcWithSideEffectsThatReturnsInt();
}

but it would be confusing because semicolons are almost never used in Swift.

An alternative that would work today is just relying on assignment returning Void, so you can write

if condition {
    _ = funcWithSideEffectsThatReturnsInt()
}

but that looks kind of weird and would probably also be confusing. Better than the semicolon rule I think, but still not great.

Another option is to check if the return value is actually used anywhere, and if it's not, then silently coerce it to Void. This way you can write

if condition {
    funcWithSideEffectsThatReturnsInt()
} else {
    funcWithSideEffectsThatReturnsString()
}

and it would be fine but writing

let foo = if condition {
    funcWithSideEffectsThatReturnsInt()
} else {
    funcWithSideEffectsThatReturnsString()
}

would fail with a type error.

I suspect that this is the right approach, but it does involve a bit of magic.

-Kevin

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

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

I like this too, seems more powerful. Also, would single line expressions be allowed? If not would case be required for example:

let myFavoriteColor = yourFavoriteColor ?
    case .Blue: .Red
    case .Green: .Blue
    case .Red: .Green
    default: .Yellow

···

On Dec 6, 2015, at 9:11 AM, Sean Heber via swift-evolution <swift-evolution@swift.org> wrote:

I really like this train of thought. +1

l8r
Sean

On Dec 6, 2015, at 11:02 AM, Alex Lew via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

What if we left the if { ...} else { ... } syntax alone (as a statement), and updated the ternary expression to be a more general pattern matching expression (closer to "switch")? Something like

let x = condition ?
   true: "Hello"
   false: "Goodbye"

let x = optionalValue ?
   .Some(let unwrapped): "Hello, \(unwrapped)"
   .None: "To Whom It May Concern"

let myFavoriteColor = yourFavoriteColor ?
    .Blue: .Red
    .Green: .Blue
    .Red: .Green

let quadrant = (x, y) ?
    let (x, y) where x < 50 && y < 50: "top left"
    let (x, y) where x < 50 && y > 50: "bottom left"
    let (x, y) where x > 50 && y < 50: "top right"
    default: "bottom right"

The colon comes from the fact that this is sort of a light-weight expression-based "switch" statement, where each branch can only contain an expression, not a series of statements.

This is very similar to pattern matching expressions in languages like Haskell, ML, and Coq.

On Sun, Dec 6, 2015 at 11:25 AM, Thorsten Seitz <thorsten.seitz@web.de <mailto:thorsten.seitz@web.de>> wrote:

Am 06.12.2015 um 01:28 schrieb Alex Lew via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

I don't think you can just get rid of the if statement in favor of an expression. You still want to be able to do this:

if (condition) {
    funcWithSideEffectsThatReturnsInt()
} else {
    funcWithSideEffectsThatReturnsString()
}

but that's not a valid expression (what is its type?).

That would actually be no problem if Swift’s type system would have union types (Ceylon has union and intersection types which are quite awesome and enable lots of nice things quite naturally, see http://ceylon-lang.org/documentation/1.2/tour/types/\).

In that case the type of such an expression would just be the union of both types, which is written Int | String in Ceylon.

-Thorsten

_______________________________________________
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

I really like this train of thought. +1

l8r
Sean

···

On Dec 6, 2015, at 11:02 AM, Alex Lew via swift-evolution <swift-evolution@swift.org> wrote:

What if we left the if { ...} else { ... } syntax alone (as a statement), and updated the ternary expression to be a more general pattern matching expression (closer to "switch")? Something like

let x = condition ?
   true: "Hello"
   false: "Goodbye"

let x = optionalValue ?
   .Some(let unwrapped): "Hello, \(unwrapped)"
   .None: "To Whom It May Concern"

let myFavoriteColor = yourFavoriteColor ?
    .Blue: .Red
    .Green: .Blue
    .Red: .Green

let quadrant = (x, y) ?
    let (x, y) where x < 50 && y < 50: "top left"
    let (x, y) where x < 50 && y > 50: "bottom left"
    let (x, y) where x > 50 && y < 50: "top right"
    default: "bottom right"

The colon comes from the fact that this is sort of a light-weight expression-based "switch" statement, where each branch can only contain an expression, not a series of statements.

This is very similar to pattern matching expressions in languages like Haskell, ML, and Coq.

On Sun, Dec 6, 2015 at 11:25 AM, Thorsten Seitz <thorsten.seitz@web.de> wrote:

Am 06.12.2015 um 01:28 schrieb Alex Lew via swift-evolution <swift-evolution@swift.org>:

I don't think you can just get rid of the if statement in favor of an expression. You still want to be able to do this:

if (condition) {
    funcWithSideEffectsThatReturnsInt()
} else {
    funcWithSideEffectsThatReturnsString()
}

but that's not a valid expression (what is its type?).

That would actually be no problem if Swift’s type system would have union types (Ceylon has union and intersection types which are quite awesome and enable lots of nice things quite naturally, see http://ceylon-lang.org/documentation/1.2/tour/types/\).

In that case the type of such an expression would just be the union of both types, which is written Int | String in Ceylon.

-Thorsten

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

What if we left the if { ...} else { ... } syntax alone (as a statement),
and updated the ternary expression to be a more general pattern matching
expression (closer to "switch")? Something like

let x = condition ?
   true: "Hello"
   false: "Goodbye"

let x = optionalValue ?
   .Some(let unwrapped): "Hello, \(unwrapped)"
   .None: "To Whom It May Concern"

let myFavoriteColor = yourFavoriteColor ?
    .Blue: .Red
    .Green: .Blue
    .Red: .Green

let quadrant = (x, y) ?
    let (x, y) where x < 50 && y < 50: "top left"
    let (x, y) where x < 50 && y > 50: "bottom left"
    let (x, y) where x > 50 && y < 50: "top right"
    default: "bottom right"

The colon comes from the fact that this is sort of a light-weight
expression-based "switch" statement, where each branch can only contain an
expression, not a series of statements.

This is very similar to pattern matching expressions in languages like
Haskell, ML, and Coq.

···

On Sun, Dec 6, 2015 at 11:25 AM, Thorsten Seitz <thorsten.seitz@web.de> wrote:

Am 06.12.2015 um 01:28 schrieb Alex Lew via swift-evolution < > swift-evolution@swift.org>:

I don't think you can just get rid of the if statement in favor of an
expression. You still want to be able to do this:

if (condition) {
    funcWithSideEffectsThatReturnsInt()
} else {
    funcWithSideEffectsThatReturnsString()
}

but that's not a valid expression (what is its type?).

That would actually be no problem if Swift’s type system would have union
types (Ceylon has union and intersection types which are quite awesome and
enable lots of nice things quite naturally, see
http://ceylon-lang.org/documentation/1.2/tour/types/\).

In that case the type of such an expression would just be the union of
both types, which is written Int | String in Ceylon.

-Thorsten

To elaborate, the reason I like this is that I think it fits with Swift's
general pattern (no pun intended) of extending familiar C syntactic forms
to work with functional features. C's enums become Algebraic Datatypes; C's
switch statements become pattern-matching statements; C's if and for and
while statements are also updated with pattern-matching abilities.

The ternary operator is C's answer to the "if-as-an-expression" question.
So why can't a modified version be our answer to the
"pattern-matching-as-an-expression" question?

It also has the advantage of being more explicit than the original ternary
operator -- having to label the true: and false: cases makes it clearer
what you're trying to achieve.

···

On Sun, Dec 6, 2015 at 12:02 PM, Alex Lew <alexl.mail+swift@gmail.com> wrote:

What if we left the if { ...} else { ... } syntax alone (as a statement),
and updated the ternary expression to be a more general pattern matching
expression (closer to "switch")? Something like

let x = condition ?
   true: "Hello"
   false: "Goodbye"

let x = optionalValue ?
   .Some(let unwrapped): "Hello, \(unwrapped)"
   .None: "To Whom It May Concern"

let myFavoriteColor = yourFavoriteColor ?
    .Blue: .Red
    .Green: .Blue
    .Red: .Green

let quadrant = (x, y) ?
    let (x, y) where x < 50 && y < 50: "top left"
    let (x, y) where x < 50 && y > 50: "bottom left"
    let (x, y) where x > 50 && y < 50: "top right"
    default: "bottom right"

The colon comes from the fact that this is sort of a light-weight
expression-based "switch" statement, where each branch can only contain an
expression, not a series of statements.

This is very similar to pattern matching expressions in languages like
Haskell, ML, and Coq.

On Sun, Dec 6, 2015 at 11:25 AM, Thorsten Seitz <thorsten.seitz@web.de> > wrote:

Am 06.12.2015 um 01:28 schrieb Alex Lew via swift-evolution < >> swift-evolution@swift.org>:

I don't think you can just get rid of the if statement in favor of an
expression. You still want to be able to do this:

if (condition) {
    funcWithSideEffectsThatReturnsInt()
} else {
    funcWithSideEffectsThatReturnsString()
}

but that's not a valid expression (what is its type?).

That would actually be no problem if Swift’s type system would have union
types (Ceylon has union and intersection types which are quite awesome and
enable lots of nice things quite naturally, see
http://ceylon-lang.org/documentation/1.2/tour/types/\).

In that case the type of such an expression would just be the union of
both types, which is written Int | String in Ceylon.

-Thorsten

I prefer making “case” optional if it can be accommodated, for compactness on a single line but maybe can be provided for those doing multiline so it is more explicit.

if not ? how about expression “switch?” to separate expression switch, from the switch statement? One issue is if is if ? is reserved for optionals. So:

let myColor = yourColor switch? .Blue : .Red, .Green: .Blue, .Red: .Green, default: .Yellow

or could be optionally with case with semicolons to separate, as a line continuation:

let myColor = yourColor switch? case .Blue : .Red; case .Green: .Blue; case .Red: .Green; default: .Yellow

But “case" would be mostly for multiline:

let myColor = yourColor switch?
  case .Blue : .Reds
  case .Green: .Blue
  case .Red: .Green
  default: .Yellow

could also be written as:

let myColor = yourColor switch?
  .Blue : .Reds
  .Green: .Blue
  .Red: .Green
  default: .Yellow

Also, ”if" could be expressionized:

let myColor = condition if? .Blue; .Red

···

On Dec 6, 2015, at 11:48 AM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

I am not a fan of this approach based on the ternary operator. The ternary operator is already a bit of an anomaly in that all other operators are unary or binary and do not perform any control flow (beyond possibly short circuiting an autoclosure argument).

I would much rather features that perform control flow continue to use keywords, but allow them to be expressions.

Once we have control flow expressions I would like to see the ternary operator removed from the language as it would no longer server a purpose. Removing the ternary operator seems to fit nicely with the direction to remove some features that are carried over from C-based languages but don’t necessarily fit with the direction Swift is heading.

On Dec 6, 2015, at 1:19 PM, Kevin Lundberg via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Ostensibly, case may not be necessary if you could delimit each case on one line with something (perhaps a comma, or something else if that would not fit well within the grammar):

let thisColor = thatColor ? .Blue: .Red, .Green: .Blue, .Red: .Green, default: .Yellow

On Sun, Dec 6, 2015, at 01:57 PM, Paul Ossenbruggen via swift-evolution wrote:

I like this too, seems more powerful. Also, would single line expressions be allowed? If not would case be required for example:

let myFavoriteColor = yourFavoriteColor ?
    case .Blue: .Red
    case .Green: .Blue
    case .Red: .Green
    default: .Yellow

On Dec 6, 2015, at 9:11 AM, Sean Heber via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I really like this train of thought. +1

l8r
Sean

On Dec 6, 2015, at 11:02 AM, Alex Lew via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

What if we left the if { ...} else { ... } syntax alone (as a statement), and updated the ternary expression to be a more general pattern matching expression (closer to "switch")? Something like

let x = condition ?
   true: "Hello"
   false: "Goodbye"

let x = optionalValue ?
   .Some(let unwrapped): "Hello, \(unwrapped)"
   .None: "To Whom It May Concern"

let myFavoriteColor = yourFavoriteColor ?
    .Blue: .Red
    .Green: .Blue
    .Red: .Green

let quadrant = (x, y) ?
    let (x, y) where x < 50 && y < 50: "top left"
    let (x, y) where x < 50 && y > 50: "bottom left"
    let (x, y) where x > 50 && y < 50: "top right"
    default: "bottom right"

The colon comes from the fact that this is sort of a light-weight expression-based "switch" statement, where each branch can only contain an expression, not a series of statements.

This is very similar to pattern matching expressions in languages like Haskell, ML, and Coq.

On Sun, Dec 6, 2015 at 11:25 AM, Thorsten Seitz<thorsten.seitz@web.de <mailto:thorsten.seitz@web.de>>wrote:

Am 06.12.2015 um 01:28 schrieb Alex Lew via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

I don't think you can just get rid of the if statement in favor of an expression. You still want to be able to do this:

if (condition) {
    funcWithSideEffectsThatReturnsInt()
} else {
    funcWithSideEffectsThatReturnsString()
}

but that's not a valid expression (what is its type?).

That would actually be no problem if Swift’s type system would have union types (Ceylon has union and intersection types which are quite awesome and enable lots of nice things quite naturally, seehttp://ceylon-lang.org/documentation/1.2/tour/types/).

In that case the type of such an expression would just be the union of both types, which is writtenInt | Stringin Ceylon.

-Thorsten

_______________________________________________
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

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

Yep probably does need braces: So for switch? if? suggestion i just made.

let myColor = yourColor switch? { .Blue : .Red, .Green: .Blue, .Red: .Green, default: .Yellow }

let myColor = yourColor switch? { case .Blue : .Red; case .Green: .Blue; case .Red: .Green; default: .Yellow }

let myColor = yourColor switch? {
  case .Blue : .Reds
  case .Green: .Blue
  case .Red: .Green
  default: .Yellow
}

let myColor = yourColor switch? {
  .Blue : .Reds
  .Green: .Blue
  .Red: .Green
  default: .Yellow
}

let myColor = condition if? { .Blue; .Red }

I don’t find that looks bad.

- Paul

···

On Dec 6, 2015, at 12:24 PM, Alex Lew via swift-evolution <swift-evolution@swift.org> wrote:

Thanks for the feedback, Matthew. It's sensible to me to consider dropping the ternary operator. I like it because the analogy "C's if is to Swift's if as C's ternary operator is to Swift's ternary operator" is (sort of) satisfied. But it is also confusing, both for the reasons you mention, and because ? has other meanings in Swift:

// compiler error without space betw thatColor and ?
let thisColor = thatColor?
     case .Red: .Green
     default: .Blue

On the other hand, is it really worth it to have control flow expressions if they don't let your code look nicer?

let thisColor = switch thatColor {
     case .Red:
          return .Green;
     default:
          return .Yellow;
}

really isn't much nicer than

let thisColor: Color
switch thatColor {
     case .Red:
          thisColor = .Green
     default:
          thisColor = .Yellow
}

Maybe we could do a compromise, something like

let thisColor = switch thatColor
     case .Red: .Green // must be an expression
     default: .Yellow // must be an expression

Or we could introduce a new keyword? Like match:

let thisColor = match thatColor
      case .Red: .Green // must be an expression
      default: .Yellow // must be an expression

I sort of like the new-keyword approach, because even though this is similar to a switch, it's not a switch: there's no fallthrough, you can't put statements inside, etc.

The problem with all these proposals:

let thisColor = match thatColor
     case .Red: match thatOtherColor
                           case .Blue: .Green
                           case .Pink: .Yellow
                           default: .Orange
     default: .Orange

is ambiguous. (Does case .Pink match thatColor or thatOtherColor? We can know because of exhaustiveness checking, but this won't always work.) You could solve this problem either by using parentheses around the whole expression when necessary

let thisColor = match thatColor
     case .Red: (match thatOtherColor
                           case .Blue: .Green
                           case .Pink: .Yellow
                           default: .Orange)
     default: .Orange

or by adding curly braces in again

let thisColor = match thatColor {
     case .Red: match thatOtherColor {
                           case .Blue: .Green
                           case .Pink: .Yellow
                           default: .Orange
                        }
     default: .Orange
}

But that starts to look like switch again. (Of course, the best way to handle this is as a programmer is to just switch on the tuple (thatColor, thatOtherColor), but the language should allow for nested control expressions.)

On Sun, Dec 6, 2015 at 2:48 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
I am not a fan of this approach based on the ternary operator. The ternary operator is already a bit of an anomaly in that all other operators are unary or binary and do not perform any control flow (beyond possibly short circuiting an autoclosure argument).

I would much rather features that perform control flow continue to use keywords, but allow them to be expressions.

Once we have control flow expressions I would like to see the ternary operator removed from the language as it would no longer server a purpose. Removing the ternary operator seems to fit nicely with the direction to remove some features that are carried over from C-based languages but don’t necessarily fit with the direction Swift is heading.

On Dec 6, 2015, at 1:19 PM, Kevin Lundberg via swift-evolution < swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Ostensibly, case may not be necessary if you could delimit each case on one line with something (perhaps a comma, or something else if that would not fit well within the grammar):

let thisColor = thatColor ? .Blue: .Red, .Green: .Blue, .Red: .Green, default: .Yellow

On Sun, Dec 6, 2015, at 01:57 PM, Paul Ossenbruggen via swift-evolution wrote:

I like this too, seems more powerful. Also, would single line expressions be allowed? If not would case be required for example:

let myFavoriteColor = yourFavoriteColor ?
    case .Blue: .Red
    case .Green: .Blue
    case .Red: .Green
    default: .Yellow

On Dec 6, 2015, at 9:11 AM, Sean Heber via swift-evolution < swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I really like this train of thought. +1

l8r
Sean

On Dec 6, 2015, at 11:02 AM, Alex Lew via swift-evolution < swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

What if we left the if { ...} else { ... } syntax alone (as a statement), and updated the ternary expression to be a more general pattern matching expression (closer to "switch")? Something like

let x = condition ?
   true: "Hello"
   false: "Goodbye"

let x = optionalValue ?
   .Some(let unwrapped): "Hello, \(unwrapped)"
   .None: "To Whom It May Concern"

let myFavoriteColor = yourFavoriteColor ?
    .Blue: .Red
    .Green: .Blue
    .Red: .Green

let quadrant = (x, y) ?
    let (x, y) where x < 50 && y < 50: "top left"
    let (x, y) where x < 50 && y > 50: "bottom left"
    let (x, y) where x > 50 && y < 50: "top right"
    default: "bottom right"

The colon comes from the fact that this is sort of a light-weight expression-based "switch" statement, where each branch can only contain an expression, not a series of statements.

This is very similar to pattern matching expressions in languages like Haskell, ML, and Coq.

On Sun, Dec 6, 2015 at 11:25 AM, Thorsten Seitz <thorsten.seitz@web.de <mailto:thorsten.seitz@web.de>> wrote:

Am 06.12.2015 um 01:28 schrieb Alex Lew via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

I don't think you can just get rid of the if statement in favor of an expression. You still want to be able to do this:

if (condition) {
    funcWithSideEffectsThatReturnsInt()
} else {
    funcWithSideEffectsThatReturnsString()
}

but that's not a valid expression (what is its type?).

That would actually be no problem if Swift’s type system would have union types (Ceylon has union and intersection types which are quite awesome and enable lots of nice things quite naturally, see http://ceylon-lang.org/documentation/1.2/tour/types/\).

In that case the type of such an expression would just be the union of both types, which is written Int | String in Ceylon.

-Thorsten

_______________________________________________
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

Untracked with Trackbuster <Your contacts automatically up to date | evercontact;
_______________________________________________
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

I do like the idea of leaving the if else as an statement because it
may simplify things. Ideally the new ternary syntax would exclude the
question mark and colon. Without introducing new keywords, it could work
something like the following.

let result = where condition switch x else y

let result2 = where x < y switch 1 else where y == x switch 2 else 3

···

On Sunday, December 6, 2015, Alex Lew via swift-evolution < swift-evolution@swift.org> wrote:

What if we left the if { ...} else { ... } syntax alone (as a statement),
and updated the ternary expression to be a more general pattern matching
expression (closer to "switch")? Something like

let x = condition ?
   true: "Hello"
   false: "Goodbye"

let x = optionalValue ?
   .Some(let unwrapped): "Hello, \(unwrapped)"
   .None: "To Whom It May Concern"

let myFavoriteColor = yourFavoriteColor ?
    .Blue: .Red
    .Green: .Blue
    .Red: .Green

let quadrant = (x, y) ?
    let (x, y) where x < 50 && y < 50: "top left"
    let (x, y) where x < 50 && y > 50: "bottom left"
    let (x, y) where x > 50 && y < 50: "top right"
    default: "bottom right"

The colon comes from the fact that this is sort of a light-weight
expression-based "switch" statement, where each branch can only contain an
expression, not a series of statements.

This is very similar to pattern matching expressions in languages like
Haskell, ML, and Coq.

On Sun, Dec 6, 2015 at 11:25 AM, Thorsten Seitz <thorsten.seitz@web.de > <javascript:_e(%7B%7D,'cvml','thorsten.seitz@web.de');>> wrote:

Am 06.12.2015 um 01:28 schrieb Alex Lew via swift-evolution < >> swift-evolution@swift.org
<javascript:_e(%7B%7D,'cvml','swift-evolution@swift.org');>>:

I don't think you can just get rid of the if statement in favor of an
expression. You still want to be able to do this:

if (condition) {
    funcWithSideEffectsThatReturnsInt()
} else {
    funcWithSideEffectsThatReturnsString()
}

but that's not a valid expression (what is its type?).

That would actually be no problem if Swift’s type system would have union
types (Ceylon has union and intersection types which are quite awesome and
enable lots of nice things quite naturally, see
http://ceylon-lang.org/documentation/1.2/tour/types/\).

In that case the type of such an expression would just be the union of
both types, which is written Int | String in Ceylon.

-Thorsten

I am not a fan of this approach based on the ternary operator. The ternary operator is already a bit of an anomaly in that all other operators are unary or binary and do not perform any control flow (beyond possibly short circuiting an autoclosure argument).

I would much rather features that perform control flow continue to use keywords, but allow them to be expressions.

Once we have control flow expressions I would like to see the ternary operator removed from the language as it would no longer server a purpose. Removing the ternary operator seems to fit nicely with the direction to remove some features that are carried over from C-based languages but don’t necessarily fit with the direction Swift is heading.

···

On Dec 6, 2015, at 1:19 PM, Kevin Lundberg via swift-evolution <swift-evolution@swift.org> wrote:

Ostensibly, case may not be necessary if you could delimit each case on one line with something (perhaps a comma, or something else if that would not fit well within the grammar):

let thisColor = thatColor ? .Blue: .Red, .Green: .Blue, .Red: .Green, default: .Yellow

On Sun, Dec 6, 2015, at 01:57 PM, Paul Ossenbruggen via swift-evolution wrote:

I like this too, seems more powerful. Also, would single line expressions be allowed? If not would case be required for example:

let myFavoriteColor = yourFavoriteColor ?
    case .Blue: .Red
    case .Green: .Blue
    case .Red: .Green
    default: .Yellow

On Dec 6, 2015, at 9:11 AM, Sean Heber via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I really like this train of thought. +1

l8r
Sean

On Dec 6, 2015, at 11:02 AM, Alex Lew via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

What if we left the if { ...} else { ... } syntax alone (as a statement), and updated the ternary expression to be a more general pattern matching expression (closer to "switch")? Something like

let x = condition ?
   true: "Hello"
   false: "Goodbye"

let x = optionalValue ?
   .Some(let unwrapped): "Hello, \(unwrapped)"
   .None: "To Whom It May Concern"

let myFavoriteColor = yourFavoriteColor ?
    .Blue: .Red
    .Green: .Blue
    .Red: .Green

let quadrant = (x, y) ?
    let (x, y) where x < 50 && y < 50: "top left"
    let (x, y) where x < 50 && y > 50: "bottom left"
    let (x, y) where x > 50 && y < 50: "top right"
    default: "bottom right"

The colon comes from the fact that this is sort of a light-weight expression-based "switch" statement, where each branch can only contain an expression, not a series of statements.

This is very similar to pattern matching expressions in languages like Haskell, ML, and Coq.

On Sun, Dec 6, 2015 at 11:25 AM, Thorsten Seitz<thorsten.seitz@web.de <mailto:thorsten.seitz@web.de>>wrote:

Am 06.12.2015 um 01:28 schrieb Alex Lew via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

I don't think you can just get rid of the if statement in favor of an expression. You still want to be able to do this:

if (condition) {
    funcWithSideEffectsThatReturnsInt()
} else {
    funcWithSideEffectsThatReturnsString()
}

but that's not a valid expression (what is its type?).

That would actually be no problem if Swift’s type system would have union types (Ceylon has union and intersection types which are quite awesome and enable lots of nice things quite naturally, seehttp://ceylon-lang.org/documentation/1.2/tour/types/).

In that case the type of such an expression would just be the union of both types, which is writtenInt | Stringin Ceylon.

-Thorsten

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

so the rule would have to be that the “switch" or “if" must return a value of compatible type. Not sure I love all the braces in the “if" case but it does follow the existing language rules with the exception that it must be have an else.

let thisColor = if condition { .Red } // illegal for expressions but not statements

Can still do this for statements:
if condition {
  x = 40
}

likewise:
let thisColor = if condition { .Red } else { 21 } // illegal unless thisColor is Any

unless:
let thisColor : Any = if condition { .Red } else { 21 } // illegal unless thisColor is Any

It would be nice to omit braces in this expression case but not for statements:
let thisColor = if condition .Red else .Blue

in statements braces would be required:

if condition {
  x = 32
} else {
  y = 44
}

···

> }
On Dec 6, 2015, at 1:52 PM, Alex Lew via swift-evolution <swift-evolution@swift.org> wrote:

I agree that it's simplest to just reuse switch keyword, and keep braces. +1.

Would you allow the same thing with if?

let thisColor = if condition { .Red } else { .Blue }

On Sun, Dec 6, 2015 at 4:44 PM, Rudolf Adamkovic <salutis@me.com <mailto:salutis@me.com>> wrote:
> On 06 Dec 2015, at 22:35, thorsten--- via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

>
> I would prefer the expression to match the statement. The only difference would be that all parts that were statements now have to be expressions.

+1

>
> Therefore the switch-expression should simply look like follows:
>
> let thisColor = switch thatColor {
> case .Red: .Green // must be an expression
> default: .Yellow // must be an expression
> }
>
> No returns needed in the case clauses.

This actually looks great. One simple rule and zero new keywords.

Readable and simple to learn.

Fantastic!

> Formatting this as a one-liner would just require adding semicolons (though I wouldn't recommend this).
>
> -Thorsten
> _______________________________________________
> 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