I’d like to suggest a bit of redesigning the Optional type and throwing 
functions to provide a single powerful and flexible mechanism for dealing with 
unexpected situations.

In short, The Optional would have an associated value of type Error added to 
its `none` case, which would describe the reason why the wrapped value is 
missing.

public enum Optional<Wrapped> {

        case .some(Wrapped)

        case .none(Error)

}

The Optional's ExpressibleByNilLiteral would initialize it with an error that 
corresponds to what is currently fatalError-ed as "unexpectedly found nil while 
unwrapping an Optional value".

The forced unwrapping operator (postfix `!`) would behave the same way as it 
does now, except in case of a fatal error it would print out the underlying 
error, instead of the aforementioned hard-coded string.

The optional chaining operator (postfix `?`) would behave the same way as it 
does now, except when it stops evaluating and returns the Optional, it would 
contain the error, returned by the sub-expression that failed to evaluate.

Any throwing function would be equivalent to a function that returns an 
Optional. If the function is declared as throwing and returning an Optional at 
the same time, it would be equivalent to a function returning an Optional 
Optional.

The if-let statement would bind the `let` variable to the wrapped value inside 
the "then" block and would bind it to the error in the "else" block. Chained 
else-if blocks would all be considered part of the overarching "else" block, so 
all of them would be able to access the error bound to the if-let name.

The guard-let and case-let statements are essentially just rewrites of if-let 
with some added logic.

The `try` keyword, applied to an optional would behave like this:

public func try<T>(_ optional: T?) throws -> T {
        guard let wrapped = optional else {
                throw wrapped // Remember, if-let, guard-let and case-let 
statements bind the let name to the error in case of a failure.
        }
        return wrapped
}

Multiple let bindings in a single if-let statement are essentially rewrites of 
a nested chain of if-let statements.

The `try` keyword applied to an optional would unwrap the value or throw the 
error.
The `try?` keyword applied to a throwing function call would cause any thrown 
errors to be caught and put into the returned Optional, instead of simply 
ignored.
The `try!` keyword applied to a throwing function call would behave as you'd 
expect: just like `try?` except immediately force-unwrapped.

A throwing function would be convertible to a non-throwing optional-returning 
function and vice versa.
This would allow making use of throwing functions when dealing with generics or 
protocols that allow arbitrary return types, without having to sacrifice the 
convenience of error-handling logic. Conversely, it would allow to write 
generic code that deals with any type of function without having to implement 
special cases for throwing functions. This means that the two function types 
would be interchangeable and one would be able to satisfy protocol requirements 
of the other. The `rethrows` idiom would then become a natural consequence of 
writing generic functions that may return optional and non-optional results 
just as well.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to