[swift-users] Every non-trivial Swift function should throw, right?
james at supmenow.com
Mon Mar 7 17:43:53 CST 2016
There is a proposal which is aiming to tackle a small subset of these issues
*James⎥Head of Trolls*
*james at supmenow.com <james at supmenow.com>⎥supmenow.com <http://supmenow.com>*
*Runway East *
*10 Finsbury Square*
* EC2A 1AF *
On Mon, Mar 7, 2016 at 9:44 PM, Jordan Rose via swift-users <
swift-users at swift.org> wrote:
> On Mar 5, 2016, at 16:59, Brent Royal-Gordon via swift-users <
> swift-users at swift.org> wrote:
> "so pretty much every non-trivial #swift function should throw, right?
> cheap & gives caller choice to catch, rethrow, try? or try! (4 in 1)"
> -- https://twitter.com/johnspurlock/status/704478619779866625
> Given that Swift provides multiple language-standard ways for clients to
> deal with a function marked as 'throws', it seems like almost all
> non-trivial shared functions should provide the additional information of
> the error in that standard form, instead of hiding it behind an optional
> return type
> No, I don't think so.
> First of all, there are functions which I can't imagine describing as
> trivial, but which nonetheless cannot fail except by programmer error. For
> instance, sorting can only fail if you provide a comparator which doesn't
> work properly (by, for instance, saying that both `a < b` and `b < a` are
> true). There is no error reporting needed at all for sorting, because the
> only possible errors are outright mistakes by the programmer. Those should
> be handled with preconditions, and the function itself should not signal
> the possibility of an error in any way at all.
> Secondly, there are functions which can only fail in a single, obvious
> way. For instance, the `indexOf(_:)` method can only fail by reaching the
> end of the Collection without finding an element matching its parameter. It
> could indicate this by throwing a CollectionError.NoMatch error, but that
> would be overkill; it's far easier to return an optional Int, with `nil`
> meaning "no match".
> Of course, the line between what should be optional and what should be a
> thrown error is necessarily subjective. Should `Int.init(_: String, radix:
> Int = 10)` be throwing or optional? On the one hand, all possible errors
> boil down to one: "you passed a string that isn't a number". On the other,
> in some contexts it might be helpful to know the problem is "there's a
> space at character offset X".
> But the solution to this tension cannot and should not simply be "always
> use the most heavyweight error handling mechanism available". That is the
> answer many languages offer, and we've all seen where it leads.
> Here are my rules of thumb:
> • If the error should never happen unless the programmer makes a mistake,
> use a precondition.
> • If there is only one way the error can be caused (or there is rarely any
> useful way for callers to respond to different causes differently), AND
> error conditions are so common that the error code paths ought to be given
> just as much weight as the success code paths, use an optional (or a
> • For everything else, use thrown errors. That is, errors which are
> neither impossible in well-written code, nor so common as to be equally
> likely as successes *and* without useful distinctions from one another,
> should be thrown.
> These rules are not purely mechanical; they require judgement from the
> API's designers and embed opinions about uses which may inconvenience some
> callers. But there's simply no way around that—the best APIs are almost
> always opinionated ones.
> or a bespoke error callback argument.
> This is worth discussing separately.
> I assume that you mean passing a closure to handle either success or
> failure. That's usually only done with asynchronous operations, which by
> necessity *must* communicate their result through a callback, so neither
> returning an optional nor throwing an error is available.
> However, we have conventional equivalents of both, which are used in the
> same cases. The equivalent of returning an optional/boolean is passing a
> single optional/boolean to the completion, and the equivalent of throwing
> is passing both an optional/boolean and an optional error to the
> completion. The throwing equivalent could be expressed a little more
> cleanly if we had a Result/Either type in the standard library, but we
> currently don't, so we can't.
> Some developers prefer to pass separate success and failure closures. I've
> never been a fan of this approach except when writing functional-style APIs
> on a type like Result, where you can use it to arbitrarily chain operations
> together. Otherwise I think it fights the language—for instance, it's not
> compatible with trailing closure syntax.
> I have high hopes that a future version of Swift will either formalize the
> success/failure pattern in callbacks, or provide some way to avoid having
> to write callbacks explicitly, just as Swift 2 formalized the old "return
> optional and have an error out parameter" pattern into the current throwing
> system. But we're not there yet and we won't be until at least Swift 4, so
> until then, we'll have to make do with awkward multiple-parameter patterns.
> There's some more expansion on different kinds of errors and the Swift
> model both in last year's WWDC presentation
> <https://developer.apple.com/videos/play/wwdc2015/106/?time=1816> (look
> for "There are really three different ways that functions can fail") and in
> the internal docs
> <https://github.com/apple/swift/blob/master/docs/ErrorHandling.rst> in
> the Swift repo. (And again with more detail in the original "rationale"
> swift-users mailing list
> swift-users at swift.org
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the swift-users