Last active
February 28, 2020 03:57
-
-
Save Qata/e5f5beadabd820ec2ad22487ab2b7c02 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
import Foundation | |
public enum Either<Left, Right> { | |
case left(Left) | |
case right(Right) | |
public var left: Left? { | |
switch self { | |
case let .left(value): | |
return value | |
case .right: | |
return nil | |
} | |
} | |
public var right: Right? { | |
switch self { | |
case .left: | |
return nil | |
case let .right(value): | |
return value | |
} | |
} | |
/// Applies the transform function to the `.left` value, if it is set. | |
/// | |
/// let e = Either<String, Int>.left("hello") | |
/// let g = e.mapLeft { $0 + " world" } | |
/// let h = g.mapRight { $0 + 1 } | |
/// g == h == .left("hello world") | |
/// | |
/// - Parameter transform: The transformative function. | |
/// - Returns: A transformed `Either`. | |
public func mapLeft<NewLeft>(_ transform: (Left) throws -> NewLeft) rethrows -> Either<NewLeft, Right> { | |
switch self { | |
case let .left(a): | |
return try .left(transform(a)) | |
case let .right(b): | |
return .right(b) | |
} | |
} | |
/// Applies the transform function to the `.right` value, if it is set. | |
/// | |
/// let e = Either<String, Int>.right(10) | |
/// let g = e.mapLeft { $0 + " world" } | |
/// let h = g.mapRight { $0 + 1 } | |
/// g == h == .right(11) | |
/// | |
/// - Parameter transform: The transformative function. | |
/// - Returns: A transformed `Either`. | |
public func mapRight<NewRight>(_ transform: (Right) throws -> NewRight) rethrows -> Either<Left, NewRight> { | |
switch self { | |
case let .left(a): | |
return .left(a) | |
case let .right(b): | |
return try .right(transform(b)) | |
} | |
} | |
/// Applies the transform function to the `.left` value, if it is set. | |
/// | |
/// let e = Either<String, Int>.left("hello") | |
/// let g = e.flatMapLeft { .left([$0]) } | |
/// g == .left(["hello"]) | |
/// | |
/// - Parameter transform: The transformative function. | |
/// - Returns: A transformed `Either`. | |
public func flatMapLeft<NewLeft>(_ transform: (Left) throws -> Either<NewLeft, Right>) rethrows -> Either<NewLeft, Right> { | |
switch self { | |
case .left(let a): | |
return try transform(a) | |
case .right(let b): | |
return .right(b) | |
} | |
} | |
/// Applies the transform function to the `.right` value, if it is set. | |
/// | |
/// let e = Either<String, Int>.right(10) | |
/// let g = e.flatMapRight { .left(String($0) + " world") } | |
/// g == .left("10 world") | |
/// | |
/// - Parameter transform: The transformative function. | |
/// - Returns: A transformed `Either`. | |
public func flatMapRight<NewRight>(_ transform: (Right) throws -> Either<Left, NewRight>) rethrows -> Either<Left, NewRight> { | |
switch self { | |
case .left(let a): | |
return .left(a) | |
case .right(let b): | |
return try transform(b) | |
} | |
} | |
} | |
public extension Either where Left == Right { | |
/// Consolidate into the underlying concrete type when both types match. | |
/// | |
/// let e = Either<String, Int>.right(10) | |
/// let f = e.mapLeft { Int($0) ?? 0 } | |
/// let g = e.mapRight { String($0) + " world" } | |
/// f.consolidated == 10 | |
/// g.consolidated == "10 world" | |
/// | |
/// - Returns: The contained value, whether `.left` or `.right`. | |
var consolidated: Left { | |
switch self { | |
case let .left(left): | |
return left | |
case let .right(right): | |
return right | |
} | |
} | |
} | |
extension Either: Equatable where Left: Equatable, Right: Equatable { | |
public static func ==(lhs: Either<Left, Right>, rhs: Either<Left, Right>) -> Bool { | |
switch (lhs, rhs) { | |
case let (.left(x), .left(y)): | |
return x == y | |
case let (.right(x), .right(y)): | |
return x == y | |
default: | |
return false | |
} | |
} | |
} | |
extension Either: Decodable where Left: Decodable, Right: Decodable { | |
public init(from decoder: Decoder) throws { | |
let container = try decoder.singleValueContainer() | |
self = try (try? container.decode(Left.self)) | |
.map(Either.left) | |
?? .right(container.decode(Right.self)) | |
} | |
} | |
extension Either: Encodable where Left: Encodable, Right: Encodable { | |
public func encode(to encoder: Encoder) throws { | |
var container = encoder.singleValueContainer() | |
switch self { | |
case let .left(left): | |
try container.encode(left) | |
case let .right(right): | |
try container.encode(right) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment