More convenient syntax ideas?

Hi everyone!

This post originated from here.

The main question is, given our "WTF Operator", we'd like to avoid this await parantheses await syntax, but with my CTO we ran out of ideas.

So, does anyone have any tips, ideas, or anything how could we better approach this?

let _ = try await (await stub(nil)) ?!  StubError.error

Thanks!

P.S.: I'm new to this forum thing so don't shread me into pieces if smthn is not according to convention ¯_(ツ)_/¯

2 Likes

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.

4 Likes

Well, it looks like we found a compiler bug then, I guess :smile:
Which way forward?

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.

You can file an issue on our Github project: Issues · apple/swift · GitHub

1 Like

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.

Yes, arguably a bug, but by design:

6 Likes

Wow. Thank you everyone for the Swift reply!
*yes, pun intended.

I'll file a bug ticket. Meanwhile we'll try to find a different api for such cases then.

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.

1 Like

What is the purpose of making the optional parameter an autoclosure if the closure is always evaluated?

4 Likes

Edit: See my next post for an implementation of something better than what I originally said here.

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.

1 Like

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:

try await { try await stub(nil).unwrapped } ?! StubError.error

(Or, if autoclosures worked…)

try await stub(nil).unwrapped ?! StubError.error

Optional.unwrapped is not new.

Optional.unwrapped
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.

Short Answer

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

@anon9791410 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. :pray:

3 Likes