How can I implement a custom error throwing syntax in swift? - swift

I want to implement the following:
throwingFunction()??.doStuff()
/* if throwingFunction throws an error:
print the error
else
returns an object with the doStuff() Method
*/
throwingFunction()??
/*
if an error is thrown,
prints the error.
else
execute the function without errors.
*/
I'm not sure where to look in the source code for examples on how do, try, catch were implemented. The Swift error docs explain how to use error handle methods that are already implemented. To be clear, I want to implement custom error handling with the above syntax.
Something like:
precedencegroup Chaining {
associativity: left
}
infix operator ?? : Chaining
extension Result {
// ERROR: Unary operator implementation must have a 'prefix' or 'postfix' modifier
static func ??(value: Result<Success, Failure>) -> Success? {
switch value {
case .success(let win):
return win
case .failure(let fail):
print(fail.localizedDescription)
return nil
}
}
}

You can define a postfix operator which takes a throwing closure as (left) operand. ?? is already defined as an infix operator, therefore you have to choose a different name:
postfix operator <?>
postfix func <?><T>(expression: () throws -> T) -> T? {
do {
return try expression()
} catch {
print(error)
return nil
}
}
Now you can call
let result = throwingFunc<?>
or chain it with
let result = (throwingFunc<?>)?.doStuff()
Previous answer:
?? is already defined as an infix operator. For a postfix operator you have to choose a different name, for example:
postfix operator <?>
extension Result {
static postfix func <?>(value: Result) -> Success? {
switch value {
case .success(let win):
return win
case .failure(let fail):
print(fail.localizedDescription)
return nil
}
}
}
Now you can call
let res = Result(catching: throwingFunc)<?>
or chain it with
let res = (Result(catching: throwingFunc)<?>)?.doStuff()

There is little chance that you can do this, without actually forking apple/swift and creating your own version of the compiler... Here are my attempts:
First, I noticed that the second part of the desired result, ?.doStuff() looks exactly like a optional chaining expression. I thought I could make a postfix ? operator that returned an optional. But it turns out, I can't declare an ? operator at all:
postfix operator ? // error
So I used a visually similar character - ‽ - instead. The type of a throwing function is () throws -> Void and I used #autoclosure so that the {} can be omitted:
typealias ThrowingFunction<T> = () throws -> T
postfix operator ‽
postfix func ‽<T>(lhs: #autoclosure ThrowingFunction<T>) -> T? {
switch Result(catching: lhs) {
case .failure(let error):
print(error.localizedDescription)
return nil
case .success(let t):
return t
}
}
// Usage:
func f() throws -> Int {
throw URLError(URLError.badURL)
}
// You have to use it like this :(
(try f()‽)
(try f()‽)?.description
The try could be omitted if the function you are calling takes no arguments:
f‽
(f‽)?.description
To make functions of other arity work without try, you need to create an implementation of ‽ for each arity, which sucks.
But the brackets must be there because of how Swift parses operators :(
Then I tried to make the approach you attempted, with key paths:
func ??<T, U>(lhs: #autoclosure ThrowingFunction<T>, rhs: KeyPath<T, U>) -> U? {
switch Result(catching: lhs) {
case .failure(let error):
print(error.localizedDescription)
return nil
case .success(let t):
return t[keyPath: rhs]
}
}
func f() throws -> Int {
throw URLError(URLError.badServerResponse)
}
This seems to be even worse, because you gotta use it like this:
try f() ?? \.description
You can't omit the try,
f ?? \.description // type inferencer freaks out, thinks T is ThrowingFunction<Int>
nor reduce the spaces on either side of ?? (See here for why):
try f()??\.description
Plus there is this backlash that is an integral part of the keypath syntax, and you can only use it for key paths, not methods. :(
Summary
You can't do this because:
You can't overload ?
You can't put a ? right after anything because it will be parsed as optional chaining
You must write try, unless you cater for every arity.

postfix operator *
#discardableResult
postfix func *<Preferred>(expression: ErrorAlt<Preferred>) -> Preferred? {
switch expression {
case .preferred(let pref):
return pref
case .error(let err):
print(err.localizedDescription)
return nil
case .initializersWereNil:
print("initializersWereNil")
return nil
}
}
Here is an example usage.
enum TestMeError: Error {
case first
}
extension Int {
func printWin() {
print("we did it!")
}
}
func testMe() -> ErrorAlt<Int> {
if true {
return .error(TestMeError.first)
} else {
return .preferred(40)
}
}
// USAGE
testMe()*

Related

How to differentiate what generic value is what in Swift?

How can I differentiate what generic value is for what in Swift?
For example, what does the value 'T' do and what does the value 'E' do?
func ??<T, E>(result: Result<T, E>, handleError: (E) -> T) -> T {
switch result {
case let .success(value):
return value
case let .failure(error):
return handleError(error)
}
}
what does the value 'T' do and what does the value 'E' do?
They're not "values", they're the names of types — on a par with a term like String or Int. In fact, T could be String or Int. But E has to be some type of Error.
So the phrase <T, E>, which appears twice, just refers to the fact that T and E are generic placeholders for their real types. When someone actually calls this ?? function, the caller will make clear what T and E really are. This is called resolving the generic.
So let's imagine that we call ?? in such a way as to resolve T to String and E to Error. Then in the mind of the compiler, we'll have this:
func ??(result: Result<String, Error>, handleError: (Error) -> String) -> String {
switch result {
case let .success(value):
return value
case let .failure(error):
return handleError(error)
}
}
So now we can read the function declaration. It says: "You hand me two parameters. One, result:, must be a Result enum whose success type is String and whose failure type is Error. The other, handleError:, must be a function that takes an Error and returns a String. And I will return a String to you."
Except, of course, that that is only one way out of an infinite number of ways to resolve T and E. They stand in for the real types that will be resolved a compiled time, depending on how ?? is actually called. So there's your answer; that is "what they do". They are placeholders standing for the real types that will be resolved at compile time.
And in fact, to demonstrate, I will call your function in a way that resolves T to String and E to Error (though I will rename your function myFunc to make the name legal):
func myFunc<T, E>(result: Result<T, E>, handleError: (E) -> T) -> T {
switch result {
case let .success(value):
return value
case let .failure(error):
return handleError(error)
}
}
enum MyError : Error { case oops }
let r = Result<String, Error> { throw MyError.oops }
let output = myFunc(result:r) { err in "Ooops" }
print(output) // Ooops
Footnote: Note that your function cannot really be called ??, as that name is taken. It might have been better to call it foo (the usual nonsense name in these situations).
#matt's answer nails it, so I thought I'd add a like bit of higher level commentary.
Type parameter naming
The use of single characters for these generic type parameters is bit of a legacy a convention from Java and C#, but it need not be so concise. In this example, if you look at the main constraint on the types (Result), note that they use Success and Failure. Using these here would provide a clearer idea about the intent of this function:
func ??<Success, Failure>(result: Result<Success, Failure>, handleError: (Failure) -> Success) -> Success
Thus, a function that takes:
a Result that can either contain a Success or a Failure, and
a closure that takes a Failure and returns a Success
and returns:
a Success
Implementations bourne out of type system constraints
Note that as none of these types are wrapped in Optional the implementation of this function is almost entirely constrained (side-effects notwithstanding).
Take, for example, the simplest function that could seemingly match:
func ?? <Success, Failure>(result: Result<Success, Failure>, handleError: (Failure) -> Success) -> Success {
return Success()
}
Attempting to compile this gives the following error:
main.swift:2:12: error: type 'Success' has no member 'init'
return Success()
^~~~~~~
due to the fact that the Success type is entirely unconstrained in Result, so we don't actually know how to create one inside the function.
We know that Result can contain a Success, so what if we try and forcibly get that?
func ?? <Success, Failure>(result: Result<Success, Failure>, handleError: (Failure) -> Success) -> Success {
return result.get()
}
This now fails with the following compiler error:
main.swift:2:12: error: call can throw, but it is not marked with 'try' and the error is not handled
return result.get()
^
due to the fact that this function has explicitly denoted that it will not throw, and Result.get() will throw the contained Failure if it does not contain a Success.
The other way to get the Success out of a Result is pattern matching, let's see how that works out matching the enumeration case pattern for a single if:
func ?? <Success, Failure>(result: Result<Success, Failure>, handleError: (Failure) -> Success) -> Success {
if case let .success(success) = result {
return success
}
}
When attempting this, the compilation error is (quite sensibly):
main.swift:5:1: error: missing return in a function expected to return 'Success'
}
^
So we need to also handle the case when a Result contains a Failure. Let's try with another pattern match, using the provided handleError closure:
func ?? <Success, Failure>(result: Result<Success, Failure>, handleError: (Failure) -> Success) -> Success {
if case let .success(success) = result {
return success
}
if case let .failure(failure) = result {
return handleError(failure)
}
}
This still gives the same error as the previous attempt, as the logic could still fall through in this case (this seems unlikely, but this could be subject to a kind of time-of-check to time-of-use bug).
Let's attempt it again, but matching both patterns in a switch:
func ?? <Success, Failure>(result: Result<Success, Failure>, handleError: (Failure) -> Success) -> Success {
switch result {
case let .success(success):
return success
case let .failure(failure):
return handleError(failure)
}
}
There we go, compiling without error.
As is obvious, this is the original function, as provided in your question.
Now that we've arrived back here, can we trim down this implementation? We could try something like:
func ?? <Success, Failure>(result: Result<Success, Failure>, handleError: (Failure) -> Success) -> Success {
switch result {
case let .success(success):
return success
}
}
but this provides this compilation error:
main.swift:2:5: error: switch must be exhaustive
switch result {
^
main.swift:2:5: note: add missing case: '.failure(_)'
switch result {
^
which indicates that we need that .failure(_) case to match all possible outcomes of the result.

Swift `Failure` keyword meaning in a Swift Combine chain error

Let's say that I've this simple chain that from an HTTP request creates a publisher for <T, APIManagerError>
func run<T:Decodable>(request:URLRequest)->AnyPublisher<T, APIManagerError>{
return URLSession.shared.dataTaskPublisher(for: request)
.map{$0.data}
.decode(type: T.self, decoder: JSONDecoder())
.eraseToAnyPublisher()// it should run mapError before this point
}
This code produces this error since I'm returning Error instead of APIManagerError.
Cannot convert return expression of type
'AnyPublisher<T, Publishers.Decode<Upstream, Output, Coder>.Failure>'
(aka 'AnyPublisher<T, Error>')
to return type 'AnyPublisher<T, RestManagerError>'
I know that to fix the issue I need to add a mapError after .decode.
.mapError{error in
APIManagerError.error("Decode Fail")
}
but I can't really understand what is reported with the error message before the "aka" part that is quite clear instead
How do you read the error Publishers.Decode<Upstream, Output, Coder>.Failure? specifically what does it mean the .Failure part? where can I find the Failure in the Swift Doc?
Since we are talking about two different kinds of errors here lets denote a Compilation Error as CE and the Failure of a Publisher (conforming to Swift.Error) as PF (Publisher Failure).
Your question is about the interpretation of the CE message.
Cannot convert return expression of type
'AnyPublisher<T, Publishers.Decode<Upstream, Output, Coder>.Failure>'
Writes out the resulting returned type of your implementation of func run - without the mapError call. The compiler acknowledges your call to eraseToAnyPublisher() in the end of the function, and also your generic Output of type T. So that covers the Cannot convert return expression of type 'AnyPublisher<T,. As to Publishers.Decode<Upstream, Output, Coder>.Failure>' outputs the derived type of the Failure. This is somewhat a symbolic breakdown of the derived Failure type. Your upstream Publisher is initially of type URLSession.DataTaskPublisher, as a result of your URLSession.shared.dataTaskPublisher call, which you then transform with every Combine operator you call: map and then decode. Resulting in the publisher Publishers.Decode. And the Failure type of cannot be properly "desymbolised" (I lack the proper compiler knowledge to use the correct terminology).
Which Xcode version do you use? The New Diagnostic Architecture might be able to show a better error message. This is in fact the reason why I later in my response use .assertMapError(is: DecodingError.self)
Your code in mapError does the job, but it completely tossed away information about the actual error. So I would not do that. At least print (log) the error. But still I would do something like:
Declare custom Error type
Intuitively we have at least two different kinds of errors, either networking or decoding. But potentially more...
public enum HTTPError: Swift.Error {
indirect case networkingError(NetworkingError)
indirect case decodingError(DecodingError)
}
public extension HTTPError {
enum NetworkingError: Swift.Error {
case urlError(URLError)
case invalidServerResponse(URLResponse)
case invalidServerStatusCode(Int)
}
}
You might need to tell combine that the error type is indeed DecodingError, thus I've declared some fatalError macros useful for this information. It is somewhat simular to Combine's setFailureType (but which only works when the upstream publisher has Failure type Never, thus we cannot use it here).
castOrKill
func typeErasureExpected<T>(
instance incorrectTypeOfThisInstance: Any,
toBe expectedType: T.Type,
_ file: String = #file,
_ line: Int = #line
) -> Never {
let incorrectTypeString = String(describing: Mirror(reflecting: incorrectTypeOfThisInstance).subjectType)
fatalError(
"Incorrect implementation: Expected variable '\(incorrectTypeOfThisInstance)' (type: '\(incorrectTypeString)') to be of type `\(expectedType)`",
file, line
)
}
func castOrKill<T>(
instance anyInstance: Any,
toType: T.Type,
_ file: String = #file,
_ line: Int = #line
) -> T {
guard let instance = anyInstance as? T else {
typeErasureExpected(instance: anyInstance, toBe: T.self, file, line)
}
return instance
}
And then create a convenience method on Publisher, similar to setFailureType:
extension Publisher {
func assertMapError<NewFailure>(is newFailureType: NewFailure.Type) -> AnyPublisher<Output, NewFailure> where NewFailure: Swift.Error {
return self.mapError { castOrKill(instance: $0, toType: NewFailure.self) }.eraseToAnyPublisher()
}
}
Usage:
I took the liberty of catching some more errors in your examples. Asserting e.g. that the server responds with a non failure HTTP status code etc.
func run<Model>(request: URLRequest) -> AnyPublisher<Model, HTTPError> where Model: Decodable {
URLSession.shared
.dataTaskPublisher(for: request)
.mapError { HTTPError.NetworkingError.urlError($0) }
.tryMap { data, response -> Data in
guard let httpResponse = response as? HTTPURLResponse else {
throw HTTPError.NetworkingError.invalidServerResponse(response)
}
guard case 200...299 = httpResponse.statusCode else {
throw HTTPError.NetworkingError.invalidServerStatusCode(httpResponse.statusCode)
}
return data
}
.decode(type: Model.self, decoder: JSONDecoder())
// It's unfortunate that Combine does not pick up that failure type is `DecodingError`
// thus we have to manually tell the Publisher this.
.assertMapError(is: DecodingError.self)
.mapError { HTTPError.decodingError($0) }
.eraseToAnyPublisher()
}
Bonus - equality check for HTTPError
It is indeed very advantageous if our error types are Equatable, it makes writing unit tests so much easier. Either we go the Equatable route, or we can do some reflection magic. I will present both solutions, but the Equatable solution is more robust for sure.
Equatable
In order to make HTTPError conform to Equatable we only need to manually make DecodingError equatable. I've done this with this code:
extension DecodingError: Equatable {
public static func == (lhs: DecodingError, rhs: DecodingError) -> Bool {
switch (lhs, rhs) {
/// `typeMismatch` is an indication that a value of the given type could not
/// be decoded because it did not match the type of what was found in the
/// encoded payload. As associated values, this case contains the attempted
/// type and context for debugging.
case (
.typeMismatch(let lhsType, let lhsContext),
.typeMismatch(let rhsType, let rhsContext)):
return lhsType == rhsType && lhsContext == rhsContext
/// `valueNotFound` is an indication that a non-optional value of the given
/// type was expected, but a null value was found. As associated values,
/// this case contains the attempted type and context for debugging.
case (
.valueNotFound(let lhsType, let lhsContext),
.valueNotFound(let rhsType, let rhsContext)):
return lhsType == rhsType && lhsContext == rhsContext
/// `keyNotFound` is an indication that a keyed decoding container was asked
/// for an entry for the given key, but did not contain one. As associated values,
/// this case contains the attempted key and context for debugging.
case (
.keyNotFound(let lhsKey, let lhsContext),
.keyNotFound(let rhsKey, let rhsContext)):
return lhsKey.stringValue == rhsKey.stringValue && lhsContext == rhsContext
/// `dataCorrupted` is an indication that the data is corrupted or otherwise
/// invalid. As an associated value, this case contains the context for debugging.
case (
.dataCorrupted(let lhsContext),
.dataCorrupted(let rhsContext)):
return lhsContext == rhsContext
default: return false
}
}
}
extension DecodingError.Context: Equatable {
public static func == (lhs: DecodingError.Context, rhs: DecodingError.Context) -> Bool {
return lhs.debugDescription == rhs.debugDescription
}
}
Which, as you can see, also has to make DecodingError.Context Equatable.
Then you can declare these XCTest helpers:
func XCTAssertThrowsSpecificError<ReturnValue, ExpectedError>(
file: StaticString = #file,
line: UInt = #line,
_ codeThatThrows: #autoclosure () throws -> ReturnValue,
_ error: ExpectedError,
_ message: String = ""
) where ExpectedError: Swift.Error & Equatable {
XCTAssertThrowsError(try codeThatThrows(), message, file: file, line: line) { someError in
guard let expectedErrorType = someError as? ExpectedError else {
XCTFail("Expected code to throw error of type: <\(ExpectedError.self)>, but got error: <\(someError)>, of type: <\(type(of: someError))>")
return
}
XCTAssertEqual(expectedErrorType, error, line: line)
}
}
func XCTAssertThrowsSpecificError<ExpectedError>(
_ codeThatThrows: #autoclosure () throws -> Void,
_ error: ExpectedError,
_ message: String = ""
) where ExpectedError: Swift.Error & Equatable {
XCTAssertThrowsError(try codeThatThrows(), message) { someError in
guard let expectedErrorType = someError as? ExpectedError else {
XCTFail("Expected code to throw error of type: <\(ExpectedError.self)>, but got error: <\(someError)>, of type: <\(type(of: someError))>")
return
}
XCTAssertEqual(expectedErrorType, error)
}
}
func XCTAssertThrowsSpecificErrorType<Error>(
_ codeThatThrows: #autoclosure () throws -> Void,
_ errorType: Error.Type,
_ message: String = ""
) where Error: Swift.Error & Equatable {
XCTAssertThrowsError(try codeThatThrows(), message) { someError in
XCTAssertTrue(someError is Error, "Expected code to throw error of type: <\(Error.self)>, but got error: <\(someError)>, of type: <\(type(of: someError))>")
}
}
Reflection magic
Or you can take a look at my Gist here which does not make use of Equatable at all, but can "compare" any enums errors which are not conforming to Equatable.
Usage
Together with CombineExpectation you can now write unit tests of your Combine code and compare errors more easily!
I've easily found the answer actually but I keep the question open in case someone else needed it.
The Failure keyword in this case is just the associatedtype that comes with the Publisher protocol.
In this case I'm just getting the Failure type for the Publishers.Decode, that by default is just Error.

How do you properly use a generic type in Swift's new Result type?

I'm trying to use Swift's new Result type in such a way that the type of the .success associated value is a generic. The rather contrived sample code below works, but is there a way to simplify the type casting so that the compiler can infer the correct type for T?
enum FetchError : Error {
case unknownKey
}
enum FetchKey {
case getWidth
case getName
}
func fetchValue<T>(_ key:FetchKey) -> Result<T, FetchError> {
switch key {
case .getName:
// Ideally I would like to just use: return .success("Johnny Appleseed")
return Result<String, FetchError>.success("Johnny Appleseed") as! Result<T, FetchError>
case .getWidth:
return Result<Double, FetchError>.success(25.0) as! Result<T, FetchError>
#unknown default:
return .failure(.unknownKey)
}
}
// This explicit type declaration is also required.
let r:Result<String, FetchError> = fetchValue(.getName)
print(r)
To your question about the type-casting, you can definitely simplify it:
case .getName:
return .success("Johnny Appleseed" as! T)
This is ok if asking for the wrong type should be considered a programming error (and so should crash), and the results will never come from external sources. If the data could ever come from an external source, then you should never crash in response to it being wrong.
In that case, we should model that kind of error:
enum FetchError : Error {
case unknownKey
case invalidType
}
Then you can have a syntax very close to what you like by adding a function to do the (possibly failing) type conversion:
func fetchValue<Value>(_ key:FetchKey) -> Result<Value, FetchError> {
func checkType(_ value: Any) -> Result<Value, FetchError> {
guard let value = value as? Value else { return .failure(.invalidType) }
return .success(value)
}
switch key {
case .getName: return checkType("Johnny Appleseed")
case .getWidth: return checkType(25.0)
#unknown default: return .failure(.unknownKey)
}
}
That said, I'd do it this way to avoid the ugliness of required type annotations:
func fetch<Value>(_: Value.Type, forKey key: FetchKey) -> Result<Value, FetchError> { ... }
let r = fetch(String.self, forKey: .getName)
This follows the pattern of Codable.
Here's the whole solution together in one place in a few different ways:
Returning Result
enum FetchError : Error {
case unknownKey
case invalidType
}
enum FetchKey {
case width
case name
}
func fetch<Value>(_: Value.Type, forKey key: FetchKey) -> Result<Value, FetchError> {
func checkType(_ value: Any) -> Result<Value, FetchError> {
guard let value = value as? Value else { return .failure(.invalidType) }
return .success(value)
}
switch key {
case .name: return checkType("Johnny Appleseed")
case .width: return checkType(25.0)
#unknown default: return .failure(.unknownKey)
}
}
With throws
I think this gets a little nicer if you throw rather than wrapping things into Result. It means you can more easily lift checkType into one place and gets very close to the syntax you said you wanted.
func fetch<Value>(_: Value.Type, forKey key: FetchKey) throws -> Value {
func checkType(value: () throws -> Any) throws -> Value {
guard let value = try value() as? Value else { throw FetchError.invalidType }
return value
}
return try checkType {
switch key {
case .name: return "Johnny Appleseed"
case .width: return 25.0
#unknown default: throw FetchError.unknownKey
}
}
}
With Optionals
This gets a little simpler with Optional if you don't really care about the errors.
func fetch<Value>(_: Value.Type, forKey key: FetchKey) -> Value? {
func _fetch() -> Any? {
switch key {
case .name: return "Johnny Appleseed"
case .width: return 25.0
#unknown default: return nil
}
}
return _fetch() as? Value
}
The function you are trying to create has a dynamically typed generic. This isn't possible as the Swift compiler needs to know the types for each method/variable at compile time.
Suppose you have
func whatToFetch() -> FetchKey {
// Randomly returns one of the FetchKey cases
}
let r = fetchValue(whatToFetch())
There is no way for the compiler to know the type of r before the call is made. Your example code works because your force-casts are perfectly lined up. But in a correct implementation you should not have to specify what type T is inside your generic function
In your case, it seems generics are not really needed and you could do away with protocols:
enum FetchKey {
case someStringValueKey
case someDoubleValueKey
case someModelValueKey
}
protocol FetchResult {}
extension String: FetchResult {}
extension Double: FetchResult {}
extension SomeModel: FetchResult {}
func fetch(_ key: FetchKey) -> Result<FetchResult,Error> {
switch key {
case .someStringValueKey:
return .success("")
case .someDoubleValueKey:
return .success(1.0)
case .someModelValueKey:
return .success(SomeModel())
}
}
let wtf = whatToFetch()
let r = fetch(wtf)
switch r {
case .success(let value):
switch wtf {
case .someStringValueKey:
guard let stringValue = value as? String else { return }
case .someDoubleValueKey:
guard let doubleValue = value as? Double else { return }
case .someModelValueKey:
guard let someModelValue = value as? SomeModel else { return }
}
case .failure(let error):
print("Better do more than just print on production code")
}
Trying to use an unqualified generic fetchValue<T> to do what you want probably isn’t going to work. The reason is that in a generic function, the T is specified by the caller, not the function. You’re essentially saying, “Ask fetchValue for any T you want, and it will give you a result.”
There’s missing link in the way you’ve set up your types. The red flag that’s your primary clue to this is the use of as!. That’s a sign that you are making assumptions about type relationships that you’re not telling the compiler about.
What does that mean? Well, note that with your code, let r: Result<URLConnection, FetchError> = fetchValue(.getName) also compiles — but then crashes at runtime!
One solution is to have an umbrella type that gathers all the possible result types, as Emil’s solution does. In this approach, you erase the type of the result, and ask callers to dynamically extract it. Callers have to deal with the possibility that the result might be of any type, not necessarily the one they expected. This is a common pattern when dealing with dynamically structured data such as JSON documents.
A second approach is to do what Rob suggests: let the caller specify the type, but translate an incorrect type into an error.
A third approach to solving this is to find a way to associate keys with result types in the type system, i.e. to tell the compiler getName → String, getWidth → Double. Unfortunately, AFAIK, there’s no way to do that with individual enum cases, so you’ll need to encode the keys as something other than an enum.
Here’s one way to do it:
enum FetchError : Error {
case unknownKey
}
protocol FetchKey {
associatedtype ValueType
func get() -> ValueType
}
struct GetNameKey: FetchKey {
func get() -> String { // This line establishes the getName → String mapping
return "Johnny Appleseed"
}
}
struct GetWidthKey: FetchKey {
func get() -> Double { // This line establishes the getWidth → Double mapping
return 25.0
}
}
// This function signature means: “If you give fetchValue a key, it
// will give you a result _with the appropriate type_ for that key”
//
func fetchValue<K: FetchKey>(_ key: K) -> Result<K.ValueType, FetchError> {
// The return type here is Result<K.ValueType, FetchError>, but
// now Swift has enough into to infer it!
return Result.success(key.get())
}
// This now works without type inference:
let r0 = fetchValue(GetNameKey())
print(r0)
// And this now (correctly) fails to compile:
// let r1: Result<Double, FetchError> = fetchValue(GetNameKey())
Which approach should you use?
Does each key always return a single, consistent type that you know beforehand (e.g. name is always a string)?
Yes: Use my approach above
No: If a fetch succeeds, but the type that came back isn’t the type the caller asked for, how do you want it reported?
As a Result.failure, and the caller can’t see the value: Use Rob’s approach
As a Result.success, and the caller has to figure out what type the value is: Use Emil’s approach

Swift closures - force a closure to always complete

Is it possible to force a closure to be completed? In the same way that a function with a return value MUST always return, it would be ace if there was a way to force a closure to contain the syntax necessary to always complete.
For example, this code will not compile because the function does not always return a value:
func isTheEarthFlat(withUserIQ userIQ: Int) -> Bool {
if userIQ > 10 {
return false
}
}
In the exact same way, I would like to define a function with a closure, which will also not compile if the closure never returns. For example, the code below might never return a completionHandler:
func isTheEarthFlat(withUserIQ userIQ: Int, completionHandler: (Bool) -> Void) {
if userIQ > 10 {
completionHandler(false)
}
}
The code above compiles, but I was wondering if there is a keyword which enforces that the closure sends a completion handler in all cases. Maybe it has something to do with the Void in the above function?
No, there is no language construct that will result in a compiler error if you forget (or don't need) to call the completion handler under all possible conditions like a return statement.
It's an interesting idea that might make a useful enhancement to the language. Maybe as a required keyword somewhere in the parameter declaration.
There is no special keyword for what you want. But there is an interesting approach you can take into consideration, that won't compile:
func isTheEarthFlat(withUserIQ userIQ: Int, completionHandler: (Bool) -> Void) {
let result: Bool
defer {
completionHandler(result)
}
if userIQ > 10 {
result = false
}
}
that will do and is completionHandler is forced to be called:
func isTheEarthFlat(withUserIQ userIQ: Int, completionHandler: (Bool) -> Void) {
let result: Bool
defer {
completionHandler(result)
}
if userIQ > 10 {
result = false
} else {
result = true
}
}
Not sure it's a good pattern to use.
Here is an interesting technique I thought of. You define GuarenteedExecution and GuarenteedExecutionResult types.
A GuarenteedExecution is a wrapper around a closure, which is to be used in a context where the execution of the closure must be guaranteed.
The GuarenteedExecutionResult is the result of executing a GuarenteedExecution. The trick is to have a desired function, e.g. isTheEarthFlat, return a GuarenteedExecutionResult. The only way to obtain a GuarenteedExecutionResult instance is by calling execute(argument:) on a GuarenteedExecution. Effectively, the type checker features responsible for guaranteeing a return, are now being used to guarantee the execution of GuarenteedExecution.
struct GuarenteedExecutionResult<R> {
let result: R
fileprivate init(result: R) { self.result = result }
}
struct GuarenteedExecution<A, R> {
typealias Closure = (A) -> R
let closure: Closure
init(ofClosure closure: #escaping Closure) {
self.closure = closure
}
func execute(argument: A) -> GuarenteedExecutionResult<R> {
let result = closure(argument)
return GuarenteedExecutionResult(result: result)
}
}
Example usage, in a seperate file (so as to not have access to GuarenteedExecutionResult.init):
let guarenteedExecutionClosure = GuarenteedExecution(ofClosure: {
print("This must be called!")
})
func doSomething(guarenteedCallback: GuarenteedExecution<(), ()>)
-> GuarenteedExecutionResult<()> {
print("Did something")
return guarenteedCallback.execute(argument: ())
}
_ = doSomething(guarenteedCallback: guarenteedExecutionClosure)

Swift 2 value extraction from Enum

Prior to Swift 2, I would often use enums with associated values and add functions to extract specific values, like so:
public enum Maybe <T> {
case Unknown
case Known(T)
public var value: T? {
switch self {
case .Unknown: return nil
case .Known(let value): return value
}
}
}
This would allow me to do something like this:
let maybe = .Known("Value")
let val = maybe.value ?? "Another Value"
I would like to get rid of these convenience functions and rely on Swift 2's new syntax. This is possible doing something like:
let val: String
if case .Known(let value) = maybe {
val = value
} else {
val = "Another Value"
}
But I can't figure out how to condense this back into a single line using the ?? operator or even ternary operator.
Is this even possible or am I stuck with defining "extraction" optionals on the enum?
Update (clarification)
The Maybe enum is just an example, but the solution would need to work on Enums that have multiple associated values... like an Either:
public enum Either<L, R> {
case Left(Box<L>)
case Right(Box<R>)
public func left() -> L?{
switch self {
case let Left(value):
return value.value
default:
return nil
}
}
public func right() -> R?{
switch self {
case let Right(value):
return value.value
default:
return nil
}
}
}
The syntax I'm looking for would be something like:
let val = (case .Known(let value) = maybe) ?? "AnotherValue"
What I want to do is easily extract an associated value for a specific case, else provide a default.
For Either it might be something like:
let val = (case .Left(let value) = either) ?? "AnotherValue"
Make sense?
The syntax you want isn't possible in Swift today (and feels unlikely for Swift tomorrow, but I often am surprised). The best available solutions are extraction functions like left() -> L? and right() -> R?. case is not a generic value-returning function that you can extend. See Rob Rix's Either for some of the best current thinking on this problem.
A key choice in Swift is that there are many statements that are not expressions. switch is one of them. Until Swift makes things like switch and if be expressions, it will be very hard to build this kind of syntax IMO.
Just define ?? for it:
func ??<T>(lhs: Maybe<T>, #autoclosure defaultValue: () throws -> T) rethrows -> T {
switch lhs {
case .Unknown: return try defaultValue()
case .Known(let value): return value
}
}
let maybe = Maybe.Known("Value")
let val = maybe ?? "Another Value"
Doing it this way gives us some nice features in Swift2. For instance, we can lazily evaluate the rhs, and we can handle throwing in the rhs:
func computeIt() -> String {
print("LAZY!")
return "Computed"
}
maybe ?? computeIt() // Does not print "LAZY!"
Maybe.Unknown ?? computeIt() // Does print "LAZY!"
enum Error: ErrorType {
case Failure
}
func fail() throws -> String {
throw Error.Failure
}
try maybe ?? fail() // Doesn't throw
do {
try Maybe.Unknown ?? fail() // throws
} catch {
print("THROW")
}