Swift Regret: try?

Part of the Swift Regrets series.

try was a useful contribution to the automatic propagation error model. try? was a mistake from start to finish, and I can say that cause most of it was me.

Somehow when the Swift error model was implemented with try and try! I became convinced that try? would be a useful “rounding out” of the keywords. I’m pretty sure I didn’t come up with it (i.e. someone else filed the Radar), but I picked it up and ran with it. It was my first time really doing anything in the SIL layer of the compiler, where code has been converted to a normalized form convenient for flow analysis. It would be try! combined with optional chaining. …I screwed it up and certain return types didn’t work for a while.

I also figured it made the most sense for try? to wrap the result as an Optional even if it was already Optional, because then you could tell errors apart from success-but-nil, a problem with some APIs in Objective-C’s manually-checked model. The community disagreed (eventually convincing me) and BJ Homer got it changed in SE-0230 to behave more like optional chaining—flatMap instead of map for you monadic folks. If the difference matters you can still use do/catch.

And by now I’ve come to believe the whole thing is a mistake. Why? Because it makes it too easy to discard error information—you should at least log it before converting to optional:

if let result = successOrLog({ try foo() })

Yes, it’s a little longer. It should be. You’re discarding error info.

There’s a specific version of this when you don’t care if an operation succeeds or fails. That really should have just been a function:

ignoringErrors { try foo() }

And even then maybe you should log.

So that’s it. From top to bottom I wish I hadn’t added try?, and present-me would have argued against a proposal. It’s occasionally useful, but it’s not an operation that needs or deserves privileged syntax built in to the language.

P.S. What about try!? A function can do that too. But I think try! is valuable for the same reason ! is: sometimes you know that with these particular inputs an operation cannot fail, and you need to tell the compiler that, and that does deserve a standard spelling. Working in a Rust codebase that requires expect instead of unwrap has provided me additional sympathy towards the !! camp in Swift, though, and there’s not a try! equivalent for that. Not sure if I’m fully convinced but I understand it better.