Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Fallible, a Swifty version of the Either monad.
import Foundation
/// Stores anything inside a reference type, even if it's a value.
public class Reference<T> {
public var value: T
public init(_ val: T) {
value = val
}
}
/// Represents the return value of an operation that might fail. For instance:
/// class func recordFromURL(url: NSURL) -> Fallible<Record>
/// A Fallible value is either successful, in which case its .value property contains the data that would normally be returned, or it is not, in which case it contains an NSError object describing what went wrong.
///
/// If the operation is a simple command (for instance, something like "writeToURL") with no natural return value, use a Fallible<Void> to indicate that the Fallible contains no data when successful.
public enum Fallible<T>: Printable {
// We use a Reference here so that all values are the same (pointer) size, thus avoiding a compiler bug.
case Success (Reference<T>)
case Failure (NSError)
// This simply hides the Reference.
public init(success: T) {
self = .Success(Reference(success))
}
// For symmetry.
public init(failure: NSError) {
self = .Failure(failure)
}
/// Returns true if the Fallible operation succeeded, or False if it failed.
public var successful: Bool {
switch self {
case .Success:
return true
case .Failure:
return false
}
}
/// Retrieves the value, if any, from the Fallible instance. If the operation failed, returns nil.
public var value: T? {
return onFailure { _ in }
}
/// Retrieves the error, if any, from the Fallible instance. If the opration succeeded, returns nil.
public var error: NSError? {
switch self {
case .Success:
return nil
case .Failure(let error):
return error;
}
}
/// Returns the value, if any, from the Fallible instance. If the operation failed, runs the provided closure, passing the error to it, and returns nil.
public func onFailure(handler: (NSError) -> Void) -> T? {
switch self {
case .Success(let ref):
return ref.value
case .Failure(let error):
handler(error)
return nil
}
}
/// Returns a value for the Fallible instance. If the operation failed, runs the provided closure, passing the error to it, and returns its return value, which should be treated as a replacement value.
public func recover(handler: (NSError) -> T) -> T {
switch self {
case .Success(let ref):
return ref.value
case .Failure(let error):
return handler(error)
}
}
/// Forces the value out of the Fallible instance. If the operation failed, this is a fatal error.
public var requiredValue: T {
switch self {
case .Success(let ref):
return ref.value
case .Failure(let error):
fatalError("Asserted success of unsuccessful Fallible value")
}
}
public var description: String {
switch self {
case .Success(let ref):
return "Success: \(ref.value)"
case .Failure(let error):
return "Failure: \(error)"
}
}
}
public func ??<T> (fallible: Fallible<T>, fallback: @autoclosure () -> T) -> T {
return fallible.recover { _ in fallback() }
}
// It would be nice to do this instead of .requiredValue, but overloading ! is forbidden.
//public postfix func !<T> (fallible: Fallible<T>) -> T {
// if let value = fallible.value {
// return value
// }
// else {
// fatalError("Asserted success of unsuccessful Fallible value")
// }
//}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.