Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce the responsibility of ResultProtocol. #235

Merged
merged 2 commits into from
Jun 9, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a separate, breaking change. We should probably do this separately?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're all for this change, so I'm okay with just doing it here.

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