bignerdranch / Freddy

A reusable framework for parsing JSON in Swift.
MIT License
1.09k stars 119 forks source link

SubscriptingOption for nil on conversion failure? #168

Closed cpboyd closed 8 years ago

cpboyd commented 8 years ago

Resolution: Use try? to convert a T throws function into one that returns an Optional<T>.


I saw a similar suggestion was already brought up in #162, but closed by the author.

I acknowledge that it'd generally be best to properly handle conversion failures and greatly appreciate that Freddy throws detailed errors.

That being said, it might be useful for handling potentially-mangled user data to allow easily returning nil (especially if the key is already treated as an optional one with .MissingKeyBecomesNil).

If there's no desire to notify the user of the specific cause for failure in parsing a particular optional key, then handling any case of failure (whether it be a missing key or incorrect type) could easily be handled by simply checking if the value is nil rather than catching a thrown error.

It also seems in-line with some Swift downcasting conventions that I grew familiar with when using Dictionary rather than Freddy. (i.e. downcasting AnyObject with as? would give nil)

jeremy-w commented 8 years ago

Could you please provide a code sample demonstrating the problem and your proposed solution? That would do a lot to anchor this conversation in concrete terms.

I took a stab at an answer, but I'm not sure we're talking about the same thing. In the off chance I guessed right, here's what I wrote at first:

If you want to convert thrown errors to nil, that's a very general utility you could use anywhere, rather than something Freddy-specific. I'd expect it'd be somthing along the lines of:

func optionally<T>(f: @noescape () -> T throws) -> T? {
    do { return try f() }
    catch { return nil }
}

Though now I've written that, I'm not clear how what you're asking for is different than try?. I think I just wrote the long version of:

func optionally<T>(f: @noescape () -> T throws) -> T? {
    return try? f()
}
jeremy-w commented 8 years ago

I looked at the linked issue #162, and that provided a bit more detail. It looks like there's a confusion in terms going on, between on the one hand "throwing errors" (which I thought we were talking about, and which Swift has rich syntactic support for handling) and on the other hand "triggering an exception" (which terminates the process generally, and which Swift has zero support for handling).

A concrete code sample demonstrating the problem (and preferably another demonstrating a solution) would be a big help.

cpboyd commented 8 years ago

Ah, try? seems to be exactly what I wanted.

cpboyd commented 8 years ago

Re-opening to clarify what I was suggesting:

func optionally<T>(f: @noescape () -> T throws) -> T? {
    do { return try f() }
    catch JSON.Error.ValueNotConvertible { return nil }
}

try? will work for my case (and probably in general).

Having a SubscriptingOption would, essentially, give the ability to do an as? instead of as! without potentially ignoring other errors as try? would.

Say, someone wanted to throw an error (and handle it with a user-displayed message or something) on missing keys, but was OK with nil on conversion failure.

Granted, that's probably a fairly obscure use-case.

jeremy-w commented 8 years ago

Thanks for clarifying.

As you've demonstrated, solving that is nearly a one-liner. It's the kernel of a very generic helper function that only ties back to JSON parsing in its impetus for creation: You could probably generalize that function to a generic "transform any of this set of errors into a nil return value" helper method, something along the lines of treatingAsNil<T>(errorTypes: Set<ErrorType.self>, f: @noescape () -> T throws) -> T?.

I hope there's a home for such a generic tool somewhere (Result?), but I don't think Freddy is that forever home. :)