Skip to content

Commit

Permalink
Merge pull request #235 from andersio/remove-result-protocol
Browse files Browse the repository at this point in the history
Reduce the responsibility of `ResultProtocol`.
  • Loading branch information
mdiep authored Jun 9, 2017
2 parents 6e5bd8d + 5baafe6 commit 6081af7
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 43 deletions.
21 changes: 13 additions & 8 deletions Result/Result.swift
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
// Copyright (c) 2015 Rob Rix. All rights reserved.

/// An enum representing either a failure with an explanatory error, or a success with a result value.
public enum Result<T, Error: Swift.Error>: ResultProtocol, CustomStringConvertible, CustomDebugStringConvertible {
case success(T)
public enum Result<Value, Error: Swift.Error>: ResultProtocol, CustomStringConvertible, CustomDebugStringConvertible {
case success(Value)
case failure(Error)

// MARK: Constructors

/// Constructs a success wrapping a `value`.
public init(value: T) {
public init(value: Value) {
self = .success(value)
}

Expand All @@ -18,17 +18,17 @@ public enum Result<T, Error: Swift.Error>: ResultProtocol, CustomStringConvertib
}

/// Constructs a result from an `Optional`, failing with `Error` if `nil`.
public init(_ value: T?, failWith: @autoclosure () -> Error) {
public init(_ value: Value?, failWith: @autoclosure () -> Error) {
self = value.map(Result.success) ?? .failure(failWith())
}

/// Constructs a result from a function that uses `throw`, failing with `Error` if throws.
public init(_ f: @autoclosure () throws -> T) {
public init(_ f: @autoclosure () throws -> Value) {
self.init(attempt: f)
}

/// Constructs a result from a function that uses `throw`, failing with `Error` if throws.
public init(attempt f: () throws -> T) {
public init(attempt f: () throws -> Value) {
do {
self = .success(try f())
} catch var error {
Expand All @@ -42,7 +42,7 @@ public enum Result<T, Error: Swift.Error>: ResultProtocol, CustomStringConvertib
// MARK: Deconstruction

/// Returns the value from `success` Results or `throw`s the error.
public func dematerialize() throws -> T {
public func dematerialize() throws -> Value {
switch self {
case let .success(value):
return value
Expand All @@ -54,7 +54,7 @@ public enum Result<T, Error: Swift.Error>: ResultProtocol, CustomStringConvertib
/// Case analysis for Result.
///
/// Returns the value produced by applying `ifFailure` to `failure` Results, or `ifSuccess` to `success` Results.
public func analysis<Result>(ifSuccess: (T) -> Result, ifFailure: (Error) -> Result) -> Result {
public func analysis<Result>(ifSuccess: (Value) -> Result, ifFailure: (Error) -> Result) -> Result {
switch self {
case let .success(value):
return ifSuccess(value)
Expand Down Expand Up @@ -107,6 +107,11 @@ public enum Result<T, Error: Swift.Error>: ResultProtocol, CustomStringConvertib
public var debugDescription: String {
return description
}

// MARK: ResultProtocol
public var result: Result<Value, Error> {
return self
}
}

// MARK: - Derive result from failable closure
Expand Down
48 changes: 13 additions & 35 deletions Result/ResultProtocol.swift
Original file line number Diff line number Diff line change
@@ -1,34 +1,14 @@
// Copyright (c) 2015 Rob Rix. All rights reserved.

/// A type that can represent either failure with an error or success with a result value.
/// A protocol that can be used to constrain associated types as `Result`.
public protocol ResultProtocol {
associatedtype Value
associatedtype Error: Swift.Error

/// Constructs a successful result wrapping a `value`.
init(value: Value)

/// Constructs a failed result wrapping an `error`.
init(error: Error)

/// Case analysis for ResultProtocol.
///
/// Returns the value produced by appliying `ifFailure` to the error if self represents a failure, or `ifSuccess` to the result value if self represents a success.
func analysis<U>(ifSuccess: (Value) -> U, ifFailure: (Error) -> U) -> U

/// Returns the value if self represents a success, `nil` otherwise.
///
/// A default implementation is provided by a protocol extension. Conforming types may specialize it.
var value: Value? { get }

/// Returns the error if self represents a failure, `nil` otherwise.
///
/// A default implementation is provided by a protocol extension. Conforming types may specialize it.
var error: Error? { get }
var result: Result<Value, Error> { get }
}

public extension ResultProtocol {

public extension Result {
/// Returns the value if self represents a success, `nil` otherwise.
public var value: Value? {
return analysis(ifSuccess: { $0 }, ifFailure: { _ in nil })
Expand All @@ -53,9 +33,7 @@ public extension ResultProtocol {

/// Returns a Result with a tuple of the receiver and `other` values if both
/// are `Success`es, or re-wrapping the error of the earlier `Failure`.
public func fanout<R: ResultProtocol>(_ other: @autoclosure () -> R) -> Result<(Value, R.Value), Error>
where Error == R.Error
{
public func fanout<U>(_ other: @autoclosure () -> Result<U, Error>) -> Result<(Value, U), Error> {
return self.flatMap { left in other().map { right in (left, right) } }
}

Expand All @@ -80,7 +58,7 @@ public extension ResultProtocol {
}
}

public extension ResultProtocol {
public extension Result {

// MARK: Higher-order functions

Expand All @@ -90,7 +68,7 @@ public extension ResultProtocol {
}

/// Returns this result if it is a .Success, or the given result otherwise. Equivalent with `??`
public func recover(with result: @autoclosure () -> Self) -> Self {
public func recover(with result: @autoclosure () -> Result<Value, Error>) -> Result<Value, Error> {
return analysis(
ifSuccess: { _ in self },
ifFailure: { _ in result() })
Expand All @@ -102,7 +80,7 @@ public protocol ErrorConvertible: Swift.Error {
static func error(from error: Swift.Error) -> Self
}

public extension ResultProtocol where Error: ErrorConvertible {
public extension Result where Error: ErrorConvertible {

/// Returns the result of applying `transform` to `Success`es’ values, or wrapping thrown errors.
public func tryMap<U>(_ transform: (Value) throws -> U) -> Result<U, Error> {
Expand All @@ -121,9 +99,9 @@ public extension ResultProtocol where Error: ErrorConvertible {

// MARK: - Operators

extension ResultProtocol where Value: Equatable, Error: Equatable {
extension Result where Value: Equatable, Error: Equatable {
/// Returns `true` if `left` and `right` are both `Success`es and their values are equal, or if `left` and `right` are both `Failure`s and their errors are equal.
public static func ==(left: Self, right: Self) -> Bool {
public static func ==(left: Result<Value, Error>, right: Result<Value, Error>) -> Bool {
if let left = left.value, let right = right.value {
return left == right
} else if let left = left.error, let right = right.error {
Expand All @@ -133,19 +111,19 @@ extension ResultProtocol where Value: Equatable, Error: Equatable {
}

/// Returns `true` if `left` and `right` represent different cases, or if they represent the same case but different values.
public static func !=(left: Self, right: Self) -> Bool {
public static func !=(left: Result<Value, Error>, right: Result<Value, Error>) -> Bool {
return !(left == right)
}
}

extension ResultProtocol {
extension Result {
/// Returns the value of `left` if it is a `Success`, or `right` otherwise. Short-circuits.
public static func ??(left: Self, right: @autoclosure () -> Value) -> Value {
public static func ??(left: Result<Value, Error>, right: @autoclosure () -> Value) -> Value {
return left.recover(right())
}

/// Returns `left` if it is a `Success`es, or `right` otherwise. Short-circuits.
public static func ??(left: Self, right: @autoclosure () -> Self) -> Self {
public static func ??(left: Result<Value, Error>, right: @autoclosure () -> Result<Value, Error>) -> Result<Value, Error> {
return left.recover(with: right())
}
}
Expand Down

0 comments on commit 6081af7

Please sign in to comment.