I'd like to reopen the idea of introducing the unless keyword. I can't find a proposal for introducing it on its own as a complementing opposite to if. I saw the decisions about using "unless" instead of "guard" and a discussion about postfix operators that mentioned unless; however, I couldn't find if we have discussed the merits of unless on its own.
Languages like Perl and Ruby use unless as the opposite of if; this improves readability in some cases.
For example, when some conditional logic is needed in the negative case.
if !items.isEmpty {
await process(items)
}
unless items.isEmpty {
await process(items)
}
Or even if both are handled unless, make it easier to put the more important path first, which helps readability.
I like the way it reads, except in unless…else cases - maybe it's because I'm not used to the unless keyword, but it seems to be non-trivial for my brain to understand the logical control flow. Merely being able to swap the if…else blocks is also not as compelling (and I say that as someone who's traditionally a stickler for putting the uncommon / error cases last, so that the happy path is most apparent).
I do often find myself wishing guard didn't require me to exit - or more specifically, that there were something like guard (not that anyone's suggested it here, but to be clear: I don't think it's a good idea to allow any variant of the existing guard keyword that allows escaping, because it would lead to mistakes - a separate keyword is important).
My only hesitation is that it does add another keyword, which - as intuitive as that keyword is (IMO) - does marginally increase Swift's complexity. Since the benefit is also relatively slight, I'm not sure where the net result falls.
Also, as an alternative: the not keyword, a la Python. Even after a decade of writing Swift I still occasionally write if not … out of habit / intuition / something, and it's never stopped being disappointing that Swift doesn't recognise this elegant syntax.
I like this approach better than an unless keyword. To me, negation written with a prefix ! is often too subtle when glancing at code like
if !letter.isASCII {
// ...
}
if intersection.isEmpty {
// ...
}
if !table.contains(.date) && !list.isEmpty {
// ...
}
compared to the clarity of
if not letter.isASCII {
// ...
}
if intersection.isEmpty {
// ...
}
if not table.contains(.date) && not list.isEmpty {
// ...
}
I have seen some code using a custom (the emoji) operator as an alias for ! to make the negation stand out more. The syntax coloring on this forum does something similar:
if (!isEmpty) { /* ... */ }
So, it might just be a themeing/Xcode issue. But I would welcome a not operator; not that I am expecting that to happen.
A not keyword may also do the trick. I think you've highlighted the root problem here; the ! prefix is very easily overlooked. Our team prefers == false for this reason. Maybe we could remove the ! prefix altogether in the same vein as removing the C style for loops.
I personally think unless still has a small advantage once we have multiple arguments
not is one of the commonly rejected proposals, despite its appealing to many outlook. It also would be odd to introduce just not, dismissing rest of the "family".
EDIT: yet reasons behind rejection seems to be mostly from the grammar perspective, if any – it reads much better than !
There are some operator-like keywords like as or is, but I understand the desire/requirement to keep operators syntactically separate from other symbols.
A not keyword baked into the compiler would probably work, but introduce special cases in the grammar, make not more special than other operators, and go against the notion that the standard library of Swift is no more special than regular Swift code. I think there are enough cases already where the language can do stuff that a regular programmer cannot that the last point would not bother me.
I don’t expect not to ever land, but I think it’s addressing the issues solved by unless better than unless does.
Quite like the idea in general. But is unless just a negation of if or does it have similar properties to !
Like would
if user.isAuthenticated && unless user.isDisabled {
work out would it be an error?
Because I think my example makes the code look more unclear, which would be the opposite of the idea.
Also does
unless user.isAuthenticated && user.isAdmin {
mean
if !user.isAuthenticated && !user.isAdmin {
or
if !user.isAuthenticated && user.isAdmin {
and to get the opposite is
unless user.isAuthenticated && !user.isAdmin {
necessary? Because that would be prone to big mistakes as every condition is now in reverse and you would now have to know the exact way unless works, whereas if is self explainatory. Translating my example to English would say
„Unless the user is authenticated and the user is not admin“
although it could mean
„Unless the user is authenticated and unless the user is not admin“
which 1) is a really confusing way of saying it and 2) would reverse the meaning of the sentence.
So to work imo, unless would need some pretty big restrictions e.g. only a single condition.
I think if not would have ever be introduced, than only as part of the language, not at stdlib level. It will indeed be similar to as/is in this speciality.
Agree. I am more against adding unless in general. Even though cases are familiar to everyone and it is tempting to try improving it, adding one more keyword to the family and further complicate choice of what to use is not what seems best (C++ and Ruby vibes on providing 10 different options to do the same thing).
I always use commas instead of the anachronistic && when possible, and think the inability to use a comma for that code is a good argument for unless, as long as these would all be equivalent:
if !(condition1 && condition2) { print("not both") }
if !condition1 || !condition2 { print("not both") }
Tangentially, I think you're alluding to the fact that Swift doesn't [generally] allow operators to start with letters? I've often wondered what would happen if that were lifted. e.g. it'd be so nice (and so in Swift's style of type safety) to be able to write 15W * 16h and have it just work like it obviously should (because W and h could be suffix operators that are essentially aliases for Watts(_ value: Double) and Hours(_ value: Double), or similar.
So there's at least two appealing cases for allowing operators to be words (letters).
Requiring the compiler to see the "operator" declaration to know how to parse a file would break the ability to be able to parse a Swift file without parsing all of its imports.
and I can see the logic behind that. An exception in form of a keyword would still work, but would go against the idea that whatever build-in operators can do is also doable by custom operators (same as with many other language features).
While that was an important idea at the start (for example, Int not being a separate, primitive thing like in many other languages), I think that principle should not hold back the language. And I don’t think it does, as exemplified by Sendable being a protocol that regular developers could not have written (like many other additions).
I think what is holding not back now is that
it would be strange to make such a fundamental change so late,
the uncomfortable status that ! then gets (is it legacy? some users will continue to use it, some not, causing unnecessary debates), and
specification/implementation complications.
Maybe one day SwiftUI needs something like “functions in prefix position without parentheses”, but save for that, I don’t have high hopes.
Personally, I'm in favor of both not and unless, but that's as a seasoned Swift developer for whom the addition of those constructs wouldn't be a cognitive burden. I'm sympathetic to the desire to have fewer keywords and sympathetic to the rationale against not.
I mean, language is free to provide any implementation that is possible only at the language level, otherwise we all could use LISP and implement whatever we want
Introducing word-like logical operators as keywords (oh, that's sounds crazy!) is just not for Swift, being C-like language. But not only that should hold new keywords as for now. Language has got many additions and non-trivial concepts recently, that it feels wrong to load it with more new things, either this not or unless.
Because control-flow statements such as return, in closures, do not return from the enclosing function:
func f1() {
if condition {
return // Returns from f1
}
// OK: not executed if condition is true.
assert(condition == false)
}
func f2() {
unless(condition) {
return // Returns from the closure
}
// Always executed (and can fail)
assert(condition)
}
Some programming languages can do that, such as Ruby. But not Swift.
To please both those who want a distinct keyword and those who like the generalizability of not, this is surely the moment where Swift's reserving ' can be put to its greatest use: ifn't.
By keeping it all one word, we then avoid the ambiguity about relative precedence of not when used with && and ||.