More flexible guard statement


(Jakob Egger) #1

At the moment "guard let" can only be used for unwrapping optionals. It
would be really nice if it could also be used with non-optional values.
For example, I'd like to write code like the following

guard
    let tableView = self.tableView,
    let col = tableView.columnWithIdentifier("MyColumn") where col != -1
else {
    NSBeep()
    print("an error occurred")
    return
}

This is not possible, because the second let assignment is non-optional,
so I have to write it like this:

guard let tableView = self.tableView else {
    NSBeep()
    print("an error occurred")
    return
}
let col = tableView.columnWithIdentifier("MyColumn")
guard col != -1 else {
    NSBeep()
    print("an error occurred")
    return
}

This leads to a lot of duplicated error handling code.

Ideally, I'd also like to mix let & where clauses in a single guard
statement, like this:

guard
    let tableView = self.tableView,
    let col = tableView.columnWithIdentifier("MyColumn") where col !=
    -1,
    let headerCell = tableView.tableColumns[col].headerCell as?
    MyTableHeaderCell
else {
    NSBeep()
    print("an error occurred")
    return
}

What do you think? Right now I always end up writing a lot of separate
guard statement, and I have a lot of repeated error handling code.

Best regards,
Jakob


(Arthur Ariel Sabintsev) #2

Not 100% a fan of this idea.

There’s a parallel thread going on right now about adding unless/until into the stdlib. I think that’s what you may want.

As for mixing guard and where, you can do that already.

Here’s a contrived example:

import UIKit

var a: UIView?
var b: UIView?

func contrivedExampleFunc() {

guard let a = a as? UILabel where a\.backgroundColor == \.blackColor\(\),
    let b = b as? UIButton where b\.backgroundColor == \.whiteColor\(\) else \{
        return
\}

}

Best,

Arthur / Sabintsev.com

···

On December 12, 2015 at 12:43:20 PM, Jakob Egger via swift-evolution (swift-evolution@swift.org) wrote:

At the moment "guard let" can only be used for unwrapping optionals. It
would be really nice if it could also be used with non-optional values.
For example, I'd like to write code like the following

guard
let tableView = self.tableView,
let col = tableView.columnWithIdentifier("MyColumn") where col != -1
else {
NSBeep()
print("an error occurred")
return
}

This is not possible, because the second let assignment is non-optional,
so I have to write it like this:

guard let tableView = self.tableView else {
NSBeep()
print("an error occurred")
return
}
let col = tableView.columnWithIdentifier("MyColumn")
guard col != -1 else {
NSBeep()
print("an error occurred")
return
}

This leads to a lot of duplicated error handling code.

Ideally, I'd also like to mix let & where clauses in a single guard
statement, like this:

guard
let tableView = self.tableView,
let col = tableView.columnWithIdentifier("MyColumn") where col !=
-1,
let headerCell = tableView.tableColumns[col].headerCell as?
MyTableHeaderCell
else {
NSBeep()
print("an error occurred")
return
}

What do you think? Right now I always end up writing a lot of separate
guard statement, and I have a lot of repeated error handling code.

Best regards,
Jakob
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Zef Houssney) #3

Jacob, you actually don’t need to have the redundant guard statement. If you want to assign something to a constant or variable, you just do that before the `guard let`, and you can still use it in the `where`:

let col = tableView.columnWithIdentifier("MyColumn")
guard let tableView = self.tableView where col != -1 else {
  NSBeep()
  print("an error occurred")
  return
}

Zef

···

On Dec 12, 2015, at 11:23 AM, Al Skipp via swift-evolution <swift-evolution@swift.org> wrote:

How about extending ‘NSTableView’ with a subscript?

extension NSTableView {
subscript(columnID columnID: String) -> Int? {
   get {
     let c = columnWithIdentifier(columnID)
     return c >= 0 ? c : .None
   }
}
}

tableView[columnID: "MyColumn"]

It doesn’t address the general case, but it does introduce a more Swifty way of dealing with non-existent return values in this use case and would enable you to use it with guard.

Al

On 12 Dec 2015, at 17:43, Jakob Egger via swift-evolution <swift-evolution@swift.org> wrote:

At the moment "guard let" can only be used for unwrapping optionals. It
would be really nice if it could also be used with non-optional values.
For example, I'd like to write code like the following

guard
  let tableView = self.tableView,
  let col = tableView.columnWithIdentifier("MyColumn") where col != -1
else {
  NSBeep()
  print("an error occurred")
  return
}

This is not possible, because the second let assignment is non-optional,
so I have to write it like this:

guard let tableView = self.tableView else {
  NSBeep()
  print("an error occurred")
  return
}
let col = tableView.columnWithIdentifier("MyColumn")
guard col != -1 else {
  NSBeep()
  print("an error occurred")
  return
}

This leads to a lot of duplicated error handling code.

Ideally, I'd also like to mix let & where clauses in a single guard
statement, like this:

guard
  let tableView = self.tableView,
  let col = tableView.columnWithIdentifier("MyColumn") where col !=
  -1,
  let headerCell = tableView.tableColumns[col].headerCell as?
  MyTableHeaderCell
else {
  NSBeep()
  print("an error occurred")
  return
}

What do you think? Right now I always end up writing a lot of separate
guard statement, and I have a lot of repeated error handling code.

Best regards,
Jakob
_______________________________________________
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


(Al Skipp) #4

How about extending ‘NSTableView’ with a subscript?

extension NSTableView {
  subscript(columnID columnID: String) -> Int? {
    get {
      let c = columnWithIdentifier(columnID)
      return c >= 0 ? c : .None
    }
  }
}

tableView[columnID: "MyColumn"]

It doesn’t address the general case, but it does introduce a more Swifty way of dealing with non-existent return values in this use case and would enable you to use it with guard.

Al

···

On 12 Dec 2015, at 17:43, Jakob Egger via swift-evolution <swift-evolution@swift.org> wrote:

At the moment "guard let" can only be used for unwrapping optionals. It
would be really nice if it could also be used with non-optional values.
For example, I'd like to write code like the following

guard
   let tableView = self.tableView,
   let col = tableView.columnWithIdentifier("MyColumn") where col != -1
else {
   NSBeep()
   print("an error occurred")
   return
}

This is not possible, because the second let assignment is non-optional,
so I have to write it like this:

guard let tableView = self.tableView else {
   NSBeep()
   print("an error occurred")
   return
}
let col = tableView.columnWithIdentifier("MyColumn")
guard col != -1 else {
   NSBeep()
   print("an error occurred")
   return
}

This leads to a lot of duplicated error handling code.

Ideally, I'd also like to mix let & where clauses in a single guard
statement, like this:

guard
   let tableView = self.tableView,
   let col = tableView.columnWithIdentifier("MyColumn") where col !=
   -1,
   let headerCell = tableView.tableColumns[col].headerCell as?
   MyTableHeaderCell
else {
   NSBeep()
   print("an error occurred")
   return
}

What do you think? Right now I always end up writing a lot of separate
guard statement, and I have a lot of repeated error handling code.

Best regards,
Jakob
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Jeff Kelley) #5

The first line of your example would need to use self.tableView, which is optional, so col would be optional as well. The assignment happens after the unwrap.

I find this a lot in my code where I need to chain together a string of optional unwrapping and assignment from those optionals, like so:

if let foo = foo, bar = foo.bar, baz =foo.baz {

}

In that case, if bar is a non-optional property of foo, I need to rewrite it like this:

if let foo = foo, baz = foo.baz {
  let bar = foo.bar

}

These examples aren’t too bad, but as you add more layers of unwrapping, it gets more difficult to do without multiple levels of indentation—though this is probably more a code smell than anything else.

Jeff Kelley

SlaunchaMan@gmail.com | @SlaunchaMan <https://twitter.com/SlaunchaMan> | jeffkelley.org <http://jeffkelley.org/>

···

On Dec 12, 2015, at 3:58 PM, Zef Houssney via swift-evolution <swift-evolution@swift.org> wrote:

Jacob, you actually don’t need to have the redundant guard statement. If you want to assign something to a constant or variable, you just do that before the `guard let`, and you can still use it in the `where`:

let col = tableView.columnWithIdentifier("MyColumn")
guard let tableView = self.tableView where col != -1 else {
NSBeep()
print("an error occurred")
return
}

Zef

On Dec 12, 2015, at 11:23 AM, Al Skipp via swift-evolution <swift-evolution@swift.org> wrote:

How about extending ‘NSTableView’ with a subscript?

extension NSTableView {
subscript(columnID columnID: String) -> Int? {
  get {
    let c = columnWithIdentifier(columnID)
    return c >= 0 ? c : .None
  }
}
}

tableView[columnID: "MyColumn"]

It doesn’t address the general case, but it does introduce a more Swifty way of dealing with non-existent return values in this use case and would enable you to use it with guard.

Al

On 12 Dec 2015, at 17:43, Jakob Egger via swift-evolution <swift-evolution@swift.org> wrote:

At the moment "guard let" can only be used for unwrapping optionals. It
would be really nice if it could also be used with non-optional values.
For example, I'd like to write code like the following

guard
let tableView = self.tableView,
let col = tableView.columnWithIdentifier("MyColumn") where col != -1
else {
NSBeep()
print("an error occurred")
return
}

This is not possible, because the second let assignment is non-optional,
so I have to write it like this:

guard let tableView = self.tableView else {
NSBeep()
print("an error occurred")
return
}
let col = tableView.columnWithIdentifier("MyColumn")
guard col != -1 else {
NSBeep()
print("an error occurred")
return
}

This leads to a lot of duplicated error handling code.

Ideally, I'd also like to mix let & where clauses in a single guard
statement, like this:

guard
let tableView = self.tableView,
let col = tableView.columnWithIdentifier("MyColumn") where col !=
-1,
let headerCell = tableView.tableColumns[col].headerCell as?
MyTableHeaderCell
else {
NSBeep()
print("an error occurred")
return
}

What do you think? Right now I always end up writing a lot of separate
guard statement, and I have a lot of repeated error handling code.

Best regards,
Jakob
_______________________________________________
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


(Adrian Kashivskyy) #6

Arthur,

There’s a parallel thread going on right now about adding unless/until into the stdlib. I think that’s what you may want.

`guard` cannot be compared to `if` or `unless` because it's used as an assertion with an early exit, not a condition. Consider the following:

let x: Int? = 5

guard let x = x where 1...10 ~= x else {
  return
}

// x is unwrapped and between 1 and 10
print(x)

Compare this to `unless`:

unless let x = x where 1...10 ~= x {
  // x is unwrapped and between 1 and 10
  print(x)
} else {
  return
}

As you can see, `unless` introduces two new scopes and leads to creation of an indentation, which can result in a pyramid of doom. In addition, `guard` requires you to early exit the scope using `return` or `break`, which is not the case when using `unless`.

That being said, I don't fully understand the original problem, because this is possible:

guard
  let col = tableView?.columnWithIdentifier("MyColumn"),
  let headerCell = tableView?.tableColumns[0].headerCell as? MyTableHeaderCell
  where col != -1
else {
  NSBeep()
  print("an error occurred")
  return
}

Pozdrawiam – Regards,
Adrian Kashivskyy

···

Wiadomość napisana przez Jeff Kelley via swift-evolution <swift-evolution@swift.org> w dniu 14.12.2015, o godz. 03:45:

The first line of your example would need to use self.tableView, which is optional, so col would be optional as well. The assignment happens after the unwrap.

I find this a lot in my code where I need to chain together a string of optional unwrapping and assignment from those optionals, like so:

if let foo = foo, bar = foo.bar, baz =foo.baz {

}

In that case, if bar is a non-optional property of foo, I need to rewrite it like this:

if let foo = foo, baz = foo.baz {
  let bar = foo.bar

}

These examples aren’t too bad, but as you add more layers of unwrapping, it gets more difficult to do without multiple levels of indentation—though this is probably more a code smell than anything else.

Jeff Kelley

SlaunchaMan@gmail.com <mailto:SlaunchaMan@gmail.com> | @SlaunchaMan <https://twitter.com/SlaunchaMan> | jeffkelley.org <http://jeffkelley.org/>

On Dec 12, 2015, at 3:58 PM, Zef Houssney via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Jacob, you actually don’t need to have the redundant guard statement. If you want to assign something to a constant or variable, you just do that before the `guard let`, and you can still use it in the `where`:

let col = tableView.columnWithIdentifier("MyColumn")
guard let tableView = self.tableView where col != -1 else {
NSBeep()
print("an error occurred")
return
}

Zef

On Dec 12, 2015, at 11:23 AM, Al Skipp via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

How about extending ‘NSTableView’ with a subscript?

extension NSTableView {
subscript(columnID columnID: String) -> Int? {
  get {
    let c = columnWithIdentifier(columnID)
    return c >= 0 ? c : .None
  }
}
}

tableView[columnID: "MyColumn"]

It doesn’t address the general case, but it does introduce a more Swifty way of dealing with non-existent return values in this use case and would enable you to use it with guard.

Al

On 12 Dec 2015, at 17:43, Jakob Egger via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

At the moment "guard let" can only be used for unwrapping optionals. It
would be really nice if it could also be used with non-optional values.
For example, I'd like to write code like the following

guard
let tableView = self.tableView,
let col = tableView.columnWithIdentifier("MyColumn") where col != -1
else {
NSBeep()
print("an error occurred")
return
}

This is not possible, because the second let assignment is non-optional,
so I have to write it like this:

guard let tableView = self.tableView else {
NSBeep()
print("an error occurred")
return
}
let col = tableView.columnWithIdentifier("MyColumn")
guard col != -1 else {
NSBeep()
print("an error occurred")
return
}

This leads to a lot of duplicated error handling code.

Ideally, I'd also like to mix let & where clauses in a single guard
statement, like this:

guard
let tableView = self.tableView,
let col = tableView.columnWithIdentifier("MyColumn") where col !=
-1,
let headerCell = tableView.tableColumns[col].headerCell as?
MyTableHeaderCell
else {
NSBeep()
print("an error occurred")
return
}

What do you think? Right now I always end up writing a lot of separate
guard statement, and I have a lot of repeated error handling code.

Best regards,
Jakob
_______________________________________________
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


(Arthur Ariel Sabintsev) #7

Hi Adrian,

Yes, as noted by others in a separate response, I misunderstood the
question. Like you, I still don't see a problem, as the OP is trying to
achieve something that can already be done (see one of my previous posts in
this thread).

···

On Wed, Dec 16, 2015 at 4:07 AM Adrian Kashivskyy via swift-evolution < swift-evolution@swift.org> wrote:

Arthur,

There’s a parallel thread going on right now about adding unless/until
into the stdlib. I think that’s what you may want.

`guard` cannot be compared to `if` or `unless` because it's used as an
assertion with an early exit, not a condition. Consider the following:

let x: Int? = 5

guard let x = x where 1...10 ~= x else {
return
}

// x is unwrapped and between 1 and 10

print(x)

Compare this to `unless`:

unless let x = x where 1...10 ~= x {
// x is unwrapped and between 1 and 10
print(x)
} else {
return
}

As you can see, `unless` introduces two new scopes and leads to creation
of an indentation, which can result in a pyramid of doom. In addition,
`guard` requires you to early exit the scope using `return` or `break`,
which is not the case when using `unless`.

That being said, I don't fully understand the original problem, because
this is possible:

guard
let col = tableView?.columnWithIdentifier("MyColumn"),
let headerCell
= tableView?.tableColumns[0].headerCell as? MyTableHeaderCell

where col != -1
else {
NSBeep()

print("an error occurred")

return

}

Pozdrawiam – Regards,
Adrian Kashivskyy

Wiadomość napisana przez Jeff Kelley via swift-evolution <
swift-evolution@swift.org> w dniu 14.12.2015, o godz. 03:45:

The first line of your example would need to use self.tableView, which is
optional, so col would be optional as well. The assignment happens after
the unwrap.

I find this a lot in my code where I need to chain together a string of
optional unwrapping and assignment from those optionals, like so:

if let foo = foo, bar = foo.bar, baz =foo.baz {

}

In that case, if bar is a non-optional property of foo, I need to rewrite
it like this:

if let foo = foo, baz = foo.baz {
let bar = foo.bar

}

These examples aren’t too bad, but as you add more layers of unwrapping,
it gets more difficult to do without multiple levels of indentation—though
this is probably more a code smell than anything else.

Jeff Kelley

SlaunchaMan@gmail.com | @SlaunchaMan <https://twitter.com/SlaunchaMan> |
jeffkelley.org

On Dec 12, 2015, at 3:58 PM, Zef Houssney via swift-evolution < > swift-evolution@swift.org> wrote:

Jacob, you actually don’t need to have the redundant guard statement. If
you want to assign something to a constant or variable, you just do that
before the `guard let`, and you can still use it in the `where`:

let col = tableView.columnWithIdentifier("MyColumn")
guard let tableView = self.tableView where col != -1 else {
NSBeep()
print("an error occurred")
return
}

Zef

On Dec 12, 2015, at 11:23 AM, Al Skipp via swift-evolution < > swift-evolution@swift.org> wrote:

How about extending ‘NSTableView’ with a subscript?

extension NSTableView {
subscript(columnID columnID: String) -> Int? {
  get {
    let c = columnWithIdentifier(columnID)
    return c >= 0 ? c : .None
  }
}
}

tableView[columnID: "MyColumn"]

It doesn’t address the general case, but it does introduce a more Swifty
way of dealing with non-existent return values in this use case and would
enable you to use it with guard.

Al

On 12 Dec 2015, at 17:43, Jakob Egger via swift-evolution < > swift-evolution@swift.org> wrote:

At the moment "guard let" can only be used for unwrapping optionals. It
would be really nice if it could also be used with non-optional values.
For example, I'd like to write code like the following

guard
let tableView = self.tableView,
let col = tableView.columnWithIdentifier("MyColumn") where col != -1
else {
NSBeep()
print("an error occurred")
return
}

This is not possible, because the second let assignment is non-optional,
so I have to write it like this:

guard let tableView = self.tableView else {
NSBeep()
print("an error occurred")
return
}
let col = tableView.columnWithIdentifier("MyColumn")
guard col != -1 else {
NSBeep()
print("an error occurred")
return
}

This leads to a lot of duplicated error handling code.

Ideally, I'd also like to mix let & where clauses in a single guard
statement, like this:

guard
let tableView = self.tableView,
let col = tableView.columnWithIdentifier("MyColumn") where col !=
-1,
let headerCell = tableView.tableColumns[col].headerCell as?
MyTableHeaderCell
else {
NSBeep()
print("an error occurred")
return
}

What do you think? Right now I always end up writing a lot of separate
guard statement, and I have a lot of repeated error handling code.

Best regards,
Jakob
_______________________________________________
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

--
Best,

Arthur / Sabintsev.com