Created
December 22, 2018 03:16
-
-
Save nanoxd/f42944b2da28380e01c648de41380436 to your computer and use it in GitHub Desktop.
[Either] A type representing an alternative of one of two types.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// A type representing an alternative of one of two types. | |
/// | |
/// By convention, and where applicable, `Left` is used to indicate failure, while `Right` is used to indicate success. (Mnemonic: “right” is a synonym for “correct.”) | |
/// | |
/// Otherwise, it is implied that `Left` and `Right` are effectively unordered alternatives of equal standing. | |
public enum Either<Left, Right> { | |
case left(Left) | |
case right(Right) | |
/// Returns the value of `Left` instances, or `nil` for `Right` instances. | |
public var left: Left? { | |
return either(ifLeft: Optional<Left>.some, ifRight: { _ in nil }) | |
} | |
/// Returns the value of `Right` instances, or `nil` for `Left` instances. | |
public var right: Right? { | |
return either(ifLeft: { _ in nil }, ifRight: Optional<Right>.some) | |
} | |
/// Returns true of `Left` instances, or false for `Right` instances. | |
public var isLeft: Bool { | |
return left != nil | |
} | |
/// Returns true of `Right` instances, or false for `Left` instances. | |
public var isRight: Bool { | |
return right != nil | |
} | |
/// Returns the result of applying `f` to the value of `Left`, or `g` to the value of `Right`. | |
public func either<Result>(ifLeft: (Left) throws -> Result, ifRight: (Right) throws -> Result) rethrows -> Result { | |
switch self { | |
case let .left(x): | |
return try ifLeft(x) | |
case let .right(x): | |
return try ifRight(x) | |
} | |
} | |
/// Maps `Right` values with `transform`, and re-wraps `Left` values. | |
public func map<NewRight>(_ transform: (Right) -> NewRight) -> Either<Left, NewRight> { | |
return flatMap { .right(transform($0)) } | |
} | |
/// Returns the result of applying `transform` to `Right` values, or re-wrapping `Left` values. | |
public func flatMap<NewRight>(_ transform: (Right) -> Either<Left, NewRight>) -> Either<Left, NewRight> { | |
return either( | |
ifLeft: Either<Left, NewRight>.left, | |
ifRight: transform | |
) | |
} | |
/// Maps `Left` values with `transform`, and re-wraps `Right` values. | |
public func mapLeft<NewLeft>(_ transform: (Left) -> NewLeft) -> Either<NewLeft, Right> { | |
return flatMapLeft { .left(transform($0)) } | |
} | |
/// Returns the result of applying `transform` to `Left` values, or re-wrapping `Right` values. | |
public func flatMapLeft<NewLeft>(_ transform: (Left) -> Either<NewLeft, Right>) -> Either<NewLeft, Right> { | |
return either( | |
ifLeft: transform, | |
ifRight: Either<NewLeft, Right>.right | |
) | |
} | |
/// Maps `Left` values with `left` & maps `Right` values with `right`. | |
public func bimap<NewLeft, NewRight>(leftBy lf: (Left) -> NewLeft, rightBy rf: (Right) -> NewRight) -> Either<NewLeft, NewRight> { | |
return either( | |
ifLeft: { .left(lf($0)) }, | |
ifRight: { .right(rf($0)) } | |
) | |
} | |
} | |
// MARK: - CustomStringConvertible | |
extension Either: CustomStringConvertible { | |
public var description: String { | |
return either( | |
ifLeft: { ".Left(\($0))"}, | |
ifRight: { ".Right(\($0))" }) | |
} | |
} | |
// MARK: - CustomDebugStringConvertible | |
extension Either: CustomDebugStringConvertible { | |
public var debugDescription: String { | |
return either( | |
ifLeft: { ".Left(\(String(reflecting: $0)))" }, | |
ifRight: { ".Right(\(String(reflecting: $0)))" }) | |
} | |
} | |
// MARK: - Equatable | |
extension Either: Equatable where Left: Equatable, Right: Equatable { } | |
// MARK: - Hashable | |
extension Either: Hashable where Left: Hashable, Right: Hashable { } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment