try and await are both supposed to allow for any part of their subexpression to respectively throw or suspend, so try await stub(nil) ?! StubError.error ought to work. If that isn't accepted, I suspect it's a compiler bug.
btw, I'm a champion of stumbling upon Swift compiler bugs and this is the first time I can actually report with code & everything because finally it's something open-source.
The left-hand side parameter here is defined as an @autoclosure; the async/await design doesn't permit hoisting await out of an @autoclosure—I pointed this out during the original review as a pitfall since await becomes required on the right-hand side of ||, etc.
Great!—please link the ticket back here if you could.
Since the behavior you described is explicitly described in the approved proposal, it's not a compiler bug; we'd need an Evolution proposal/amendment to revise this.
An autoclosure can have a default value. For example:
func foo(x: @autoclosure () -> Int = 5 ) { }
But it doesn't appear that @adam.rocska wants a default value for any of the parameters for the "WTF Operator," so this is not a basis to conclude that this operator is inappropriate.
You're right; I don't know what it was that was breaking for me.
I can see how someone would see it that way, but it's not an objective observation. Everybody wants the equivalent of this operator, and everybody wants a default—for unwrapping. This should not be conflated with error remapping. The standard library should include both, independently, which would support harmonious usage:
public extension Optional {
/// Represents that an `Optional` was `nil`.
struct UnwrapError: Error & Equatable {
public init() { }
}
/// - Throws: [`UnwrapError` when `nil`.](https://forums.swift.org/t/unwrap-or-throw-make-the-safe-choice-easier/14453/7)
/// - Note: Useful for emulating `break`, with `map`, `forEach`, etc.
var unwrapped: Wrapped {
get throws {
switch self {
case let wrapped?: return wrapped
case nil: throw UnwrapError()
}
}
}
}
/// Map one `Error` to another upon failure, disregarding the original.
public func ?! <Success>(
_ success: () async throws -> Success,
_ error: @autoclosure () -> some Error
) async throws -> Success {
try await Result(catching: success)
.mapError { _ in error() }
.get()
}
public extension Result where Failure == Error {
init(catching success: () async throws -> Success) async {
do {
self = .success(try await success())
} catch {
self = .failure(error)
}
}
}
Using Result for this is not necessary, but it's illustrative.
The autoclosure part is there because it keeps certain doors open.
Broader Answer
Backstory
What's important to understand about both the WTF operator, the Beton lib in general, and the whole OSS stuff of my company is that we're shoveling the fruits of our labor to the public right now as we speak.
It's a time consuming task with two engineers to go through hundreds of repos & live projects to cherrypick those that can be open-sourced, because of legal reasons, know-how protection and utility. For example there are lots of things we could put out there but I'm sure nobody would use simply because it doesn't fit how the masses think.
Specifically for this case
@Jessy is totally on the right track sniffing here. It's just that we didn't manage to put everything out yet :-/
THANK YOU
I'm honestly surprised & amazed that this small topic started such a conversation here. I'm very grateful for the help I received here & also for the truthful interest you people showed. Never would've expected this.
You really made my weekend folks! After a hard month it's good to have something positive.