Hi swift-evolution,
I've been working on a pitch I think you're all going to love, as well as the corresponding implementation
Control Flow Negation Statements
- Proposal: SE-NNNN
- Author: Harlan Haskins
- Review Manager: TBD
- Status: Awaiting review
- Implementation: apple/swift#23704
Introduction
This proposal adds negated forms of the various control-flow operations in the Swift language. Specifically, it introduces negated forms of if, guard, while, do, and defer.
Motivation
Swift is a uniquely expressive language, but there are still places where boilerplate is necessary. One egregious example of missing syntactic sugar revolves around condition negation. Frequently, developers want to conditionalize their code on a condition they intend to be false. Swift has a prefix operator, !, that allows for negating a boolean, but this can be difficult to see when in front of a large expression, and sometimes requires parentheses around nested logical expressions.
Instead, let’s provide negated forms of common control flow. While we’re at it, we can also add negated forms of do and defer, to complete the picture.
Proposed solution
I propose adding 5 new statements to Swift: ifn’t, guardn’t, whilen’t, don’t, and defern’t. These will be analogues to the if, guard, while, do, and defer statement, but with the opposite semantics.
Detailed design
ifn’t
ifnt-statement → `ifn’t` condition-list code-block else-clause?
else-clause → `else` code-block | `else` if-statement | `else` ifn’t-statement
An ifn’t statement executes the provided code block if the provided condition evaluates to false. There is no support for ifn’t let or ifn’t case statements.
guardn’t
guardnt-statement → `guardn’t` condition-list `else` code-block
A guardn’t statement executes the provided code block if the provided condition evaluates to true. There is no support for guardn’t let or ifn’t case statements. All the traditional rules of guard still apply.
whilen’t
whilent-statement → `whilen’t` condition-list code-block
A whilen’t statement executes the provided code block repeatedly until the provided condition evaluates to true. There is no support for whilen’t let or whilen’t case statements.
don’t
dont-statement → `don’t` code-block
A don’t statement does not execute the code in the provided code block.
defern’t
defernt-statement → `defern’t` code-block
A defern’t statement does not schedule the code in the provided code block to be run at the end of the current execution scope. These blocks, because they are not scheduled at all, do not execute in reverse order.
Source compatibility
These changes are purely additive, no source changes will be necessary to adopt them.
Effect on ABI stability
These statements do not meaningfully affect ABI surface.
Effect on API resilience
These statements do not affect the API of the declarations in which they are used.
Alternatives considered
There are many more statements whose semantics can be negated. They are left to future proposals, as I did not want to balloon this proposal.
ifn’t let, guardn’t let, whilen’t let
We could support parameter binding in negated statements. One possible implementation strategy is to only execute the block if the value could not be bound, and do not introduce the binding into the new scope.
ifn’t case, guardn’t case, whilen’t case
Similar to the above, we could support pattern matching. A possible strategy could be to match all patterns except the written one, and explicitly not bind any pattern bindings.
catchn’t
We could extend the existing catch clause to support catchn’t, which will only execute if the caught error does not match the provided pattern.
forn’t
It is unclear how this would work. Maybe this would execute if the sequence returned nil immediately, without any intermediate results.
return’t
This could be a no-op, or this could only work for boolean return values and negate their value.