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
This proposal adds negated forms of the various control-flow operations in the Swift language. Specifically, it introduces negated forms of
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
defer, to complete the picture.
I propose adding 5 new statements to Swift:
defern’t. These will be analogues to the
defer statement, but with the opposite semantics.
ifnt-statement → `ifn’t` condition-list code-block else-clause? else-clause → `else` code-block | `else` if-statement | `else` ifn’t-statement
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.
guardnt-statement → `guardn’t` condition-list `else` code-block
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.
whilent-statement → `whilen’t` condition-list code-block
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.
dont-statement → `don’t` code-block
don’t statement does not execute the code in the provided code block.
defernt-statement → `defern’t` code-block
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.
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.
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.
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.
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.
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.
It is unclear how this would work. Maybe this would execute if the sequence returned
nil immediately, without any intermediate results.
This could be a no-op, or this could only work for boolean return values and negate their value.