isNonEmpty

I know this is frowned upon, as we cannot create inverses for all boolean properties, but the inverse to isEmpty is used a lot and really improves readability, IMHO.

We are building and maintaining a large Swift application and have defined isNonEmpty for all types and protocols that have isEmpty, and would suggest others do the same - or preferably that it gets into the standard library :+1:

if !isEmpty {
  // use it
}

Becomes:

if isNonEmpty {
  // use it
}

Actually, IMHO, the readability of ! is exacerbated here by the initial letter of isEmpty being the visual inverse: i / ! :-)

2 Likes

This was also discussed a few months ago: `isNotEmpty` on Array - #9 by gwendal.roue (and I don't believe that's the first time).

1 Like

Thanks!

Yeah, it's obviously not a goal of the standard library to add all sorts of small convenience wrappers like this, but I think this is special in that it will really help readability. I think readability is a great argument.

If's not uncommon that you have a long expression ending in .isEmpty. You will then always have to go back to the start of the expression to check for a possible negation symbol (that does not stand-out visually). With isNonEmpty this reads fluently.

It is indeed very easy to just add isNonEmpty to each project, without any reasonable risk of getting it wrong. But if isNonEmpty was part of the standard library, it would potentially improve the readability of all Swift source code, in learning material and code examples.

I think isNonEmpty is a better name than isNotEmpty, as this is not just a negation of isEmpty; we want to check for the property "non-empty". I also think it makes the code read more like proper English.

hasElements might be less brain-taxing.

8 Likes

But does a non-empty range have elements? Perhaps it does. But a non-empty geometry does not, I think.

1 Like

I know this is a frequently suggested (and seemingly rejected) thing, but I do want to point out that at least 95% of my code which contains .isEmpty also contains a !.

We might want to consider an exception to the "we can't provide negated versions of anything/everything that returns Bool" heuristic in this case because it is so frequent...

1 Like

Your last sentance should be: "We might want to consider an exception in this case because it is so frequent in my code". Otherwise you are making a logical leap.

1 Like

I did a quick search in part of our code and got 700 occurrences or isNonEmpty and 500 occurrences of isEmpty.

I wonder what stats others have, but I would assume that with guard etc. it could easily be the case in most projects that the check for non-emptiness is more common than the check for emptiness.

Considering the alternatives, I'm in favor of adding isNonEmpty :slight_smile:

  1. Keep it as is and make
if !interestingButRatherLongExpressionHere.isEmpty {

a fine example of proper usage of Swift...

  1. Replace the isEmpty property with an isNonEmpty one, as that appears to be used more often :wink:

  2. Make it a method instead so the ! and isEmpty stay close :stuck_out_tongue:

if !isEmpty(interestingButRatherLongExpressionHere) {
  1. Add isNonEmpty

Allow

if interestingButRatherLongExpressionHere.isNonEmpty {

but face a torrent of incoming please-add-my-inversed-boolean-property-too requests...

Searching a larger part of our codebase showed 1350 occurrences of isNonEmpty and 800 occurrences of isEmpty.

That's almost 1.7 times more.

I'm not sure I'm advocating it, because I haven't given it much thought yet. But, if you want to solve the problem of the negation being visually far separated from the relevant expression, you could add some kind of extension on Bool:

extension Bool {
  var not: Bool { return !self } 
}

if viewModel.nested.collection.isEmpty.not { /* ... */ }
if model.names.contains(element).not { /* ... */ }
// ... etc
2 Likes

That does improves locality. But it doesn't read like English to me, sorry :slight_smile:

Sure, I agree. Maybe you can think of some better name?
Because if you do, you have solved it for every bool property and function returning a bool.

That's kind of true. But even if I change it to ask whether collection.isEmpty.isFalse, or whether collection.isEmpty.negated, or whatever, it's still sort of a double negative and imposes additional cognitive load if I just want to know whether the collection is non-empty.

But does a non-empty range have elements? Perhaps it does.

While you are right that we don’t think of them as elements for Range, the documentation does refer to them as such: Range | Apple Developer Documentation (even if this is because it is lifted from the protocol, which I suspect is the case).

And the associatedtype of the underlying protocol is Element.

But a non-empty geometry does not, I think.

I'm sure it won't fit as well with some potential Sequence conformances, but is there one in particular you mean?

Core team:

You can also use isEmpty == false.

1 Like

Speaking of extending Bool, we could add Bool.toggled as a non-mutating variant the recently added Bool.toggle().

Envelope.isEmpty / Envelope.isNonEmpty. Not a Sequence, but we still need to ensure it's non-empty before using it.

if collection.isEmpty.toggled { is still much worse than if collection.isNonEmpty {. But better than if !collection.isEmpty { I guess... :slight_smile:

+1 for isNotEmpty, it seems to read clear in code

if we add isNonEmpty to the standard library what will we use as our strawman example of a property that should not be added to the standard library?

10 Likes

The thing is that !isEmpty is used a lot in common code, and apparently in many cases more than isEmpty is. On top of that it really helps readability. Together it really helps readability for a lot of Swift code...

It would be great with some corpus of Swift code that could be used to make statistics for arguing these sort of things :+1:

1 Like