Skip to content

Instantly share code, notes, and snippets.

@nanoxd
Created December 22, 2018 03:16
Show Gist options
  • Save nanoxd/f42944b2da28380e01c648de41380436 to your computer and use it in GitHub Desktop.
Save nanoxd/f42944b2da28380e01c648de41380436 to your computer and use it in GitHub Desktop.
[Either] A type representing an alternative of one of two types.
/// 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