A use for semi-colons: alternating between if and if let


(Ross O'Brien) #1

Since Swift almost abolished the semi-colon as an end of statement marker,
and Swift 3 is abolishing is in C-style for-loops, I thought I'd suggest
something else for it to do.

Swift 1.2 allowed developers to pack multiple if let bindings into one if
let, using comma separators. This was well received as it removed the
disliked 'pyramid of doom' - lots of nested indented if statements.

I'd like to propose using the semi-colon to pack if lets and regular ifs
into one if. The format would essentially be:
if <boolean expression>; let x = x as? Foo; <boolean expression>; let y =
x.<property>, z = y.<property> // and so on
{
  // closure which only happens if both expressions are true and all three
bindings happen; note that the last two are comma-separated as they're both
'if let's.
}

There would only be one actual 'if'. The semi-colon
This would also be applicable to guard and guard let. I've had a number of
occasions where I've written multiple guard statements with identical else
closures. I'd like to avoid some of that duplicate code.

I realise that it's already possible to combine conditions and conditional
bindings in one if, using the 'where' keyword, but I don't think it's clear
- certainly not as clear as when where filters for-in ranges or case
statements.

For example: getting the first element from an array of optionals. Here's
my sample trivial example:

var array : [Int?] = []

if let x = array[0] where array.count > 0

{

print(x)

}
This code doesn't work. The array index is out of range. But there's no way
I know of to rearrange the 'if' to ensure the array isn't empty before
binding to the first element - the developer has to write a nested if
statement.

My suggested syntax would present the if like this:

if array.count > 0;

let x = array[0]

{

print(x)

}

The semi-colon would read as 'and' in the same way the comma does in a
multiple if let or && does in an if; it allows the developer to alternate
between boolean expressions and conditional bindings. (The downside here is
that three distinct punctuation symbols are all essentially used to mean
the same thing.)

Is this worthy of discussion?


(Dave Abrahams) #2

Its meaning is underloaded! :slight_smile:

poking-bits-into-the-lexicon-ly y'rs,

···

on Tue Feb 02 2016, Ross O'Brien <swift-evolution@swift.org> wrote:

Since Swift almost abolished the semi-colon as an end of statement marker,
and Swift 3 is abolishing is in C-style for-loops, I thought I'd suggest
something else for it to do.

--
-Dave


(Jordan Rose) #3

We already allow this, with commas:

var array: [Int?] = []
if !array.isEmpty, let x = array[0] {
  print(x)
}

Jordan

···

On Feb 2, 2016, at 16:53, Ross O'Brien via swift-evolution <swift-evolution@swift.org> wrote:

For example: getting the first element from an array of optionals. Here's my sample trivial example:
  var array : [Int?] = []
  if let x = array[0] where array.count > 0
  {
    print(x)
  }
This code doesn't work. The array index is out of range. But there's no way I know of to rearrange the 'if' to ensure the array isn't empty before binding to the first element - the developer has to write a nested if statement.


(Joe Groff) #4

That's true, but there are some problematic ambiguities with our current syntax, since 'let' and 'case' conditions both admit multiple sub-conditions also separated by commas, e.g. 'if let x = y, y = z { }' or 'if case let x = y, let y = z'. Using ';' for this purpose would be less ambiguous.

-Joe

···

On Feb 2, 2016, at 5:13 PM, Jordan Rose via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 2, 2016, at 16:53, Ross O'Brien via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

For example: getting the first element from an array of optionals. Here's my sample trivial example:
  var array : [Int?] = []
  if let x = array[0] where array.count > 0
  {
    print(x)
  }
This code doesn't work. The array index is out of range. But there's no way I know of to rearrange the 'if' to ensure the array isn't empty before binding to the first element - the developer has to write a nested if statement.

We already allow this, with commas:

var array: [Int?] = []
if !array.isEmpty, let x = array[0] {
  print(x)
}


(James Hillhouse IV) #5

Here’s a question that Drew raised as he was educating me on this discussion. Given that the following works, are you proposing to deprecate,

if let a = 2, var b = 3, let c = 4, var d = 5{ } // 1

in favor of?

if let a = 2; var b = 3; let c = 4; var d = 5{ } // 2

If semi-colon is still being considered, can Joe or Jordon clarify?

···

On Feb 2, 2016, at 7:40 PM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 2, 2016, at 5:13 PM, Jordan Rose via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Feb 2, 2016, at 16:53, Ross O'Brien via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

For example: getting the first element from an array of optionals. Here's my sample trivial example:
  var array : [Int?] = []
  if let x = array[0] where array.count > 0
  {
    print(x)
  }
This code doesn't work. The array index is out of range. But there's no way I know of to rearrange the 'if' to ensure the array isn't empty before binding to the first element - the developer has to write a nested if statement.

We already allow this, with commas:

var array: [Int?] = []
if !array.isEmpty, let x = array[0] {
  print(x)
}

That's true, but there are some problematic ambiguities with our current syntax, since 'let' and 'case' conditions both admit multiple sub-conditions also separated by commas, e.g. 'if let x = y, y = z { }' or 'if case let x = y, let y = z'. Using ';' for this purpose would be less ambiguous.


(Ross O'Brien) #6

I don't know if this will help clarify things, so take or leave as you like:

I wasn't aware before this discussion that the first of the comma-separated
expressions of an if let could be just an 'if'.

Now I have learned it, it just seems to be an odd construction: the first
clause of an if can be a boolean expression, a let binding, a var binding
or a case let/var binding, and then after the first comma every boolean
expression must be preceded by 'where'. There's no obligation for the
boolean expression to relate to the constant or variable just bound.

My original premise was simply to allow boolean expressions and binding
expressions to alternate within the if; boolean operators like & and |
would separate boolean expressions, commas would separate binding
expressions, and semi-colons would allow alternating from one to the other.
I hadn't anticipated that this would deprecate any existing code; a let
followed by a var in the if would still be comma-separated binding
expressions - but I'm not objecting to deprecating something if it makes
the code clearer.

So (and I apologise for the trivialised example) my proposed change would
be that this:

if a == 5, let b = c where array.count > 5, let d = c, var e = array[0], f
= array[1] where e == f, case .Some(let g) = c where g.characters.count > 2
{ }

would become

if a == 5; let b = c; array.count > 5; let d = c, var e = array[0], f =
array[1] where e == f; case .Some(let g) = c where g.characters.count > 2 {
}

So, mostly the same, but 'where' can be used only where it clarifies to the
reader that it's qualifying a binding, rather than being used because it's
the only way to follow a binding expression with a boolean expression.

···

On Fri, Feb 5, 2016 at 12:04 AM, James Hillhouse IV via swift-evolution < swift-evolution@swift.org> wrote:

Here’s a question that Drew raised as he was educating me on this
discussion. Given that the following works, are you proposing to deprecate,

if let a = 2, var b = 3, let c = 4, var d = 5{ } // 1

in favor of?

if let a = 2; var b = 3; let c = 4; var d = 5{ } // 2

If semi-colon is still being considered, can Joe or Jordon clarify?

On Feb 2, 2016, at 7:40 PM, Joe Groff via swift-evolution < > swift-evolution@swift.org> wrote:

On Feb 2, 2016, at 5:13 PM, Jordan Rose via swift-evolution < > swift-evolution@swift.org> wrote:

On Feb 2, 2016, at 16:53, Ross O'Brien via swift-evolution < > swift-evolution@swift.org> wrote:

For example: getting the first element from an array of optionals. Here's
my sample trivial example:
var array : [Int?] = []
if let x = array[0] where array.count > 0
{
print(x)
}
This code doesn't work. The array index is out of range. But there's no
way I know of to rearrange the 'if' to ensure the array isn't empty before
binding to the first element - the developer has to write a nested if
statement.

We already allow this, with commas:

var array: [Int?] = []
if !array.isEmpty, let x = array[0] {
  print(x)
}

That's true, but there are some problematic ambiguities with our current
syntax, since 'let' and 'case' conditions both admit multiple
sub-conditions also separated by commas, e.g. 'if let x = y, y = z { }' or
'if case let x = y, let y = z'. Using ';' for this purpose would be less
ambiguous.

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


(James Hillhouse IV) #7

Ross,

Thanks for the clarification of what you’re trying to do.

Jim

···

On Feb 4, 2016, at 6:58 PM, Ross O'Brien <narrativium@gmail.com> wrote:

I don't know if this will help clarify things, so take or leave as you like:

I wasn't aware before this discussion that the first of the comma-separated expressions of an if let could be just an 'if'.

Now I have learned it, it just seems to be an odd construction: the first clause of an if can be a boolean expression, a let binding, a var binding or a case let/var binding, and then after the first comma every boolean expression must be preceded by 'where'. There's no obligation for the boolean expression to relate to the constant or variable just bound.

My original premise was simply to allow boolean expressions and binding expressions to alternate within the if; boolean operators like & and | would separate boolean expressions, commas would separate binding expressions, and semi-colons would allow alternating from one to the other. I hadn't anticipated that this would deprecate any existing code; a let followed by a var in the if would still be comma-separated binding expressions - but I'm not objecting to deprecating something if it makes the code clearer.

So (and I apologise for the trivialised example) my proposed change would be that this:
if a == 5, let b = c where array.count > 5, let d = c, var e = array[0], f = array[1] where e == f, case .Some(let g) = c where g.characters.count > 2 { }
would become
if a == 5; let b = c; array.count > 5; let d = c, var e = array[0], f = array[1] where e == f; case .Some(let g) = c where g.characters.count > 2 { }

So, mostly the same, but 'where' can be used only where it clarifies to the reader that it's qualifying a binding, rather than being used because it's the only way to follow a binding expression with a boolean expression.

On Fri, Feb 5, 2016 at 12:04 AM, James Hillhouse IV via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Here’s a question that Drew raised as he was educating me on this discussion. Given that the following works, are you proposing to deprecate,

if let a = 2, var b = 3, let c = 4, var d = 5{ } // 1

in favor of?

if let a = 2; var b = 3; let c = 4; var d = 5{ } // 2

If semi-colon is still being considered, can Joe or Jordon clarify?

On Feb 2, 2016, at 7:40 PM, Joe Groff via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Feb 2, 2016, at 5:13 PM, Jordan Rose via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Feb 2, 2016, at 16:53, Ross O'Brien via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

For example: getting the first element from an array of optionals. Here's my sample trivial example:
  var array : [Int?] = []
  if let x = array[0] where array.count > 0
  {
    print(x)
  }
This code doesn't work. The array index is out of range. But there's no way I know of to rearrange the 'if' to ensure the array isn't empty before binding to the first element - the developer has to write a nested if statement.

We already allow this, with commas:

var array: [Int?] = []
if !array.isEmpty, let x = array[0] {
  print(x)
}

That's true, but there are some problematic ambiguities with our current syntax, since 'let' and 'case' conditions both admit multiple sub-conditions also separated by commas, e.g. 'if let x = y, y = z { }' or 'if case let x = y, let y = z'. Using ';' for this purpose would be less ambiguous.

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