Swift v2.0 Error Handling – Revisit

Last night I posted my initial thoughts about Swift's approach to error handling. I've softened a little to the approach as I got to play with it a bunch this morning, including re-visting what a Result<T, E> looks like in Swift.

It's only after going through that excercise again that reminded me just how clunky dealing with enum values are, especially those that contain values, that I can appreciate why the Swift team went in the direction they did.

enum MyCustomError: ErrorType {
    case Happy
    case Dance
}

enum Result<T, E> {
    case Ok(T)
    case Error(E)
}

func base() -> Result<(), MyCustomError> {
    return Result.Error(MyCustomError.Dance)
}

func handle() {
    let result = base()

    switch (result) {
    case .Ok(_):
        println("required...")

    case let .Error(error):
        switch (error) {
        case MyCustomError.Happy:
            print("Happy error")

        case MyCustomError.Dance:
            print("Dance error")
        }
    }
}

Or, with the Swift error handling approach.

enum MyCustomError: ErrorType {
    case Happy
    case Dance
}

func base() throws {
    throw MyCustomError.Dance
}

func handle() {
    do {
        try base()
    }
    catch MyCustomError.Happy {
        print("Happy error")
    }
    catch MyCustomError.Dance {
        print("Dance error")
    }
    catch {
        print("catch all, because no types")
    }
}

Of course… those aren't our only options though. See, there was this lovely little keyword guard that was also introduced to us. So, by putting some better smarts into Result<T, E>, we can get something that looks like this:

enum MyCustomError: ErrorType {
    case Happy
    case Dance
}

enum Result<T, E> {
    case Ok(T)
    case Err(E)

    // what if these were simply generated for all enums?
    var ok: T? {
        switch self {
        case let .Ok(value): return value
        case .Err(_): return nil
        }
    }

    var err: E? {
        switch self {
        case .Ok(_): return nil
        case let .Err(e): return e
        }
    }
}

func base() -> Result<(), MyCustomError> {
    return Result.Err(MyCustomError.Dance)
}

func handle() {
    let result = base()

    guard let value = result.ok else {
        switch result.err! {
        case MyCustomError.Happy:
            print("Happy error")

        case MyCustomError.Dance:
            print("Dance error")
        }

        return
    }

    print("value: \(value)")
}

Swift's throws approach is still more terse, but provides no type-safety on the error type. It might be possible to stream-line the approach above some more, but it feels better to me. I never liked exceptions used as code flow control and the do-try-catch approach seems to just beg for that.

Eh… I'll keep noodling.

Swift v2.0 Error Handling – Revisit