.none vs nil

This was brought up in a different thread:

This simple example illustrates the issue:

enum Variant {
    case none
    case integer
    case real
}

func foo(_ v: Variant?) {
}

foo(.none) // nil
foo(Variant.none) // Variant.none

Here passing .none maybe confused with Variant.none while it actually means nil.

In addition to the methods outlined by John above I also want to add another one, conceptually simpler (although it might be harder to swallow in practice): the very source of the issue here is the Optional case name none. If it was called, say, nil we would not have this issue. Can we deprecate Option.none and rename it to Option.nil ? Both .nil and nil would mean the same thing.

I also like the "deprecating the use of .none when nil would be allowed". Deprecate either in all cases or only in those cases when there is collision with Optional.none and SomethingElse.none.

"Improving autocompletion to not suggest .none" - would be nice, but IMHO not enough.

This code gives me a warning:

Assuming you mean 'Optional.none'; did you mean 'Variant.none' instead?

I think this warning is enough to prevent programmers from using the wrong .none case in this instance.


What if Variant had a nil case? Wouldn't there still be an issue?

2 Likes

This is conflating multiple issues which it would be responsible to lay out separately with careful study. Reflexing a core team comment immediately into a discussion about making a source- and ABI-breaking change isn’t the way to go, in my view.

@1-877-547-7272 has beat me to it pointing out some issues off the bat.

Fundamentally, it seems premature to argue an issue merits a language change because tooling and diagnostics improvements aren’t enough when the contemplated tooling and diagnostics aren’t yet implemented and when one hasn’t familiarized oneself with the tooling and diagnostics that are there.

1 Like

Another possible alternative is to make this warning an error.

It would if you deliberately name it so. NB: I found no precedents of nil or similar names (e.g. case constants ending with "Nil") in system headers, while there are many precedents of "None" in there (a few hundred).

This topic is intended to be a call for opinions to test the waters and see if we have a problem or not, it's not a pre-pitch.

To understand the nature of the problem requires study and not just opinions. It has been only four hours since the core team mentioned the issue, so to have any reasonably informed take on it we’d need a proper primer.

Can you outline your findings to date about the issue that you’ve accumulated in preparation to kick off this discussion? What has led you to conclude that the suggested diagnostics and tooling measures aren’t enough?

I first saw this issue on some older version of Xcode where warnings were not issued in this case. My manual workaround at the time was an "internal guideline" to never name my custom enum cases none; and if I had to use system's type, like UITableViewCellSelectionStyle.none in conjunction with my internal API that takes Optional of that type to always specify the full form of the constant.

Then when the warning started being issued I tried to find a way to force warning to error (without forcing all warnings to errors), and didn't find one (I remember it was possible in C + gcc via the Werror option but such thing doesn't exist in swift). Warning is good, but imho it would be better if that was error.

As I mentioned above I scanned through apple headers and found a hundred or few "None" cases (I only checked enums, probably there are more due to static members) but was't able to spot "nil".

Long before, since the inception of swift I remember wondering why do we have two different spellings .none and nil, would it not be better to have just one (e.g. .nil with dot), ditto for .false and .true making Bool a true enum, but I digress.

Having seen the core team invitation for discussion prompted me to start this topic, and I admit your judgment on the topic is valuable as in most other cases, although I didn't get whether you consider there is a problem here or not and if there is what you consider the best possible solution. As I understand you are more disagreeing "on the point of order", with the particular wording I used or the particular timing for this topic I've chosen.

To make this more constructive let me outline the options as I currently see them:

  1. there is no problem, do nothing
  2. warning is good enough
  3. make warning an error
  4. remember to check for this particular warning type (if you have many warnings it's easy to miss "important" warnings, and of course ideally there should be "zero warning policy" to allow #3).
  5. make it an error to use .none when it conflicts with other types none
  6. make it an error to use .none anywhere where nil can be
  7. deprecate and rename Option.none to Option.nil - very radical option
  8. prohibit ".none" usage in enums (very bad option, won't fly, just for completeness)
  9. improving autocompletion to not suggest .none

Being hit with this problem in the past, along with the core team I'd like to see what people think on this topic. Maybe there's another better option. I can't currently afford "making all warnings errors", if I could - that would be a good enough option for me.

Yeah, so this goes to a key part of my question--the core team has invited consideration of a range of options and you've concluded off the bat that a warning isn't good enough. I see a lot that's compelling in terms of information that moves the needle from (1) ("do nothing") to (2) "warning"—but I don't see anywhere that you address why you've already concluded that a warning isn't enough.

1 Like

When working on a project that doesn't have "zero warning tolerance policy" there could be quite some number of other warnings (maybe 10, or a 100) and among that noise it's very easy to miss "important" warnings which you've chosen to not tolerate (1).

[(1) This prompts a side question of its own: "should we have a mechanism similar to gcc -Werror= for swift for a fine grained "treat particular warning as error" setting".]

Anyone who doesn't have a zero warning policy is doing it wrong. The only acceptable warnings are ones you can't fix and can't turn off (like the deployment target warning for packages).

4 Likes

I agree and fix all warnings I can fix given constraints. The rest which I can't is the noise that prohibits me seeing "important" new warnings and prohibits me enabling "treat warnings as errors" (this is the latter what I call "zero warning tolerance policy").

When a colleague proposes an enum with a .none case, I generally recommend that he remove that case and instead wrap that enum in Optional since that will give you a .none case "for free" whilst also giving you all the built-in mechanisms for handling optionals in the language.

Rather than remove Optional.none, I would really prefer that nil is removed. To me, it doesn't feel quite right that nil values have such a prominent place in Swift, as it seems to inspire the "everything is potentially null" line of thought inherited from other languages.

5 Likes

In swift “nil” is very clearly associated with optionals, similar to how the question mark is only used in relation to optionals (that I can think of). “none” is general enough that I can see it being used as an enum case, but nil would be pretty strange because of this association.

1 Like

(And ternaries.)

While removing the none case might work sometimes, it doesn’t really work in cases where none is a value in the domain you’re modeling and you need to differentiate between the explicit value none and the absence of a value.

For example, if you’re working with CSS properties, you might model the display property as:

enum CSSDisplay {
  case none
  case block
  case flex
  ...
}

struct CSSProperties {
  var display: CSSDisplay?
}

The distinction between display: nil and display: CSSDisplay.none is important here. One means "the display property doesn’t have a value", and the other means "the display property has the CSS value none".

I think nil as a keyword actually helps here. Writing display: Optional.none vs display: CSSDisplay.none (or display: .none vs display: .some(.none)) doesn’t make this more readable or understandable.

2 Likes

That is correct, but I think the other way around is much more prevalent: A business object is represented by an enum but is sometimes not available and it so happens that the absence of the entire value and the .none value effectively have the same meaning.

But this is, like so much real world software, dependent on one's exact circumstances.