Skip to content

Instantly share code, notes, and snippets.

@Qata
Last active February 28, 2020 03:57
Show Gist options
  • Save Qata/e5f5beadabd820ec2ad22487ab2b7c02 to your computer and use it in GitHub Desktop.
Save Qata/e5f5beadabd820ec2ad22487ab2b7c02 to your computer and use it in GitHub Desktop.
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