Skip to content

Commit

Permalink
Filling out functionality, tests, and readme
Browse files Browse the repository at this point in the history
  • Loading branch information
KyNorthstar committed Sep 19, 2023
1 parent dceab50 commit b42fb5c
Show file tree
Hide file tree
Showing 9 changed files with 617 additions and 17 deletions.
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,19 @@
# Either
# Yet Another `Either` Type! 🥳

Did the world need another Swift `Either` type? No.
Are We suffering from Not-Invented-Here Syndrome? Maybe.
Did We still think this was a good idea? Definitely.

`Either` is a concept in many functional and strongly-typed languages which allows a value of **either** one type or another, to be stored in one field:

```swift
/// A response for the population query
struct PopulationResponse {

/// The list of people in the population
///
/// - Note: In 1.x, this was a list of names as `String`s.
/// In 2.x and newer, this is a map of UUIDs to `Person` objects
let people: Either<[String], [UUID: Person]>
}
```
79 changes: 69 additions & 10 deletions Sources/Either/Either + autoconformance.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import Foundation

extension Either: Equatable where Left: Equatable, Right: Equatable {

public static func == (lhs: Self, rhs: Self) -> Bool { // TODO: Test
@inlinable
public static func == (lhs: Self, rhs: Self) -> Bool {
switch (a: lhs, b: rhs) {
case (a: .left(let leftA), b: .left(let leftB)):
return leftA == leftB
Expand All @@ -27,9 +28,10 @@ extension Either: Equatable where Left: Equatable, Right: Equatable {
}


@inlinable
public static func == (lhs: Self, rhs: Self) -> Bool
where Left == Right
{ // TODO: Test
{
switch (a: lhs, b: rhs) {
case (a: .left(let a), b: .left(let b)),
(a: .right(let a), b: .right(let b)),
Expand All @@ -38,6 +40,14 @@ extension Either: Equatable where Left: Equatable, Right: Equatable {
return a == b
}
}


@inline(__always)
public static func != (lhs: Self, rhs: Self) -> Bool
where Left == Right
{
!(lhs == rhs)
}
}


Expand All @@ -46,7 +56,8 @@ extension Either: Equatable where Left: Equatable, Right: Equatable {

extension Either: Comparable where Left: Comparable, Right: Comparable {

public static func < (lhs: Self, rhs: Self) -> Bool { // TODO: Test
@inlinable
public static func < (lhs: Self, rhs: Self) -> Bool {
switch (a: lhs, b: rhs) {
case (a: .left(let leftA), b: .left(let leftB)):
return leftA < leftB
Expand All @@ -60,15 +71,63 @@ extension Either: Comparable where Left: Comparable, Right: Comparable {
}


@inlinable
public static func < (lhs: Self, rhs: Self) -> Bool
where Left == Right
{ // TODO: Test
{
switch (a: lhs, b: rhs) {
case (a: .left(let a), b: .left(let b)),
(a: .right(let a), b: .right(let b)),
(a: .left(let a), b: .right(let b)),
(a: .right(let a), b: .left(let b)):
return a < b
}
}
}



// Swift compiler requires this be a separate extension for some reason
public extension Either where Left: Comparable, Right: Comparable {

@inlinable
static func <= (lhs: Self, rhs: Self) -> Bool
where Left == Right
{
switch (a: lhs, b: rhs) {
case (a: .left(let a), b: .left(let b)),
(a: .right(let a), b: .right(let b)),
(a: .left(let a), b: .right(let b)),
(a: .right(let a), b: .left(let b)):
return a < b
return a <= b
}
}


@inlinable
static func >= (lhs: Self, rhs: Self) -> Bool
where Left == Right
{
switch (a: lhs, b: rhs) {
case (a: .left(let a), b: .left(let b)),
(a: .right(let a), b: .right(let b)),
(a: .left(let a), b: .right(let b)),
(a: .right(let a), b: .left(let b)):
return a >= b
}
}


@inlinable
static func > (lhs: Self, rhs: Self) -> Bool
where Left == Right
{
switch (a: lhs, b: rhs) {
case (a: .left(let a), b: .left(let b)),
(a: .right(let a), b: .right(let b)),
(a: .left(let a), b: .right(let b)),
(a: .right(let a), b: .left(let b)):
return a > b
}
}
}
Expand All @@ -78,7 +137,7 @@ extension Either: Comparable where Left: Comparable, Right: Comparable {
// MARK: - Hashable

extension Either: Hashable where Left: Hashable, Right: Hashable {
public func hash(into hasher: inout Hasher) { // TODO: Test
public func hash(into hasher: inout Hasher) {
switch self {
case .left(let left): left.hash(into: &hasher)
case .right(let right): right.hash(into: &hasher)
Expand All @@ -91,7 +150,7 @@ extension Either: Hashable where Left: Hashable, Right: Hashable {
// MARK: - CustomStringConvertible

extension Either: CustomStringConvertible where Left: CustomStringConvertible, Right: CustomStringConvertible {
public var description: String { // TODO: Test
public var description: String {
switch self {
case .left(let left):
return left.description
Expand All @@ -107,7 +166,7 @@ extension Either: CustomStringConvertible where Left: CustomStringConvertible, R
// MARK: - CustomDebugStringConvertible

extension Either: CustomDebugStringConvertible where Left: CustomDebugStringConvertible, Right: CustomDebugStringConvertible {
public var debugDescription: String { // TODO: Test
public var debugDescription: String {
switch self {
case .left(let left):
return left.debugDescription
Expand All @@ -134,7 +193,7 @@ private extension Either {
// MARK: - Encodable

extension Either: Encodable where Left: Encodable, Right: Encodable {
public func encode(to encoder: Encoder) throws { // TODO: Test
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKey.self)

switch self {
Expand All @@ -152,7 +211,7 @@ extension Either: Encodable where Left: Encodable, Right: Encodable {
// MARK: - Decodable

extension Either: Decodable where Left: Decodable, Right: Decodable {
public init(from decoder: Decoder) throws { // TODO: Test
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKey.self)

if let left = try container.decodeIfPresent(Left.self, forKey: .left) {
Expand Down
15 changes: 13 additions & 2 deletions Sources/Either/Either + conversions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public extension Either where Left == Void {
/// Converts an `Optional` into an `Either`, converting the `.none` case into `.left(Void())`, and `.some(right)` into `.right(right)`
///
/// - Parameter optional: The value to be converted into an `Either`
init(_ optional: Optional<Right>) { // TODO: Test
init(_ optional: Optional<Right>) {
switch optional {
case .none:
self = .left(())
Expand All @@ -24,6 +24,17 @@ public extension Either where Left == Void {
self = .right(right)
}
}


init(wrapping wrapped: Optional<Right>) {
self.init(wrapped)
}


@available(*, unavailable, message: "Ambiguous use of `Either` initializer. If you meant to wrap the given argument within another `Either`, call `.init(wrapping:)`. If you meant to create a new `Either` from an existing one, simply create a new `let` or `var` and assign the old one to the new name.")
init<Other>(_ optional: Either<Right, Other>?) {
preconditionFailure("Unavailable initializer called")
}
}


Expand Down Expand Up @@ -72,7 +83,7 @@ public extension Result {
/// The given `Either`'s `Left` will be treated as the `Success`, and the `Right` will be treated as the `Failure`, so `Right` must be an `Error`
///
/// - Parameter either: The `Either` which can be represented as a `Result<Left, Right>`
init(_ either: Either<Success, Failure>) { // TODO: Test
init(_ either: Either<Success, Failure>) {
switch either {
case .left(let left):
self = .success(left)
Expand Down
26 changes: 23 additions & 3 deletions Sources/Either/Either + unwrapping.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@ import Foundation



prefix operator *



public extension Either {

/// If the held value is the `Left` type, then it's returned. Otherwise, `nil` is returned
var left: Left? { // TODO: Test
var left: Left? {
switch self {
case .left(let left):
return left
Expand All @@ -24,7 +28,7 @@ public extension Either {


/// If the held value is the `Right` type, then it's returned. Otherwise, `nil` is returned
var right: Right? { // TODO: Test
var right: Right? {
switch self {
case .right(let right):
return right
Expand All @@ -41,15 +45,31 @@ public extension Either where Left == Right {

/// Unwraps whatever value this has, left or right
///
/// ```swift
/// var either = Either<String, String>.left("Hello")
/// print(*either) // Prints "Hello"
///
/// either = .right("World")
/// print(*either) // Prints "World"
/// ```
///
/// This is only available when the `Left` type is the same as the `Right` type
@inline(__always)
static prefix func ! (_ rhs: Self) -> Value {
static prefix func * (_ rhs: Self) -> Value {
rhs.value
}


/// Unwraps whatever value this has, left or right
///
/// ```swift
/// var either = Either<String, String>.left("Hello")
/// print(either.value) // Prints "Hello"
///
/// either = .right("World")
/// print(either.value) // Prints "World"
/// ```
///
/// This is only available when the `Left` type is the same as the `Right` type
var value: Value {
switch self {
Expand Down
Loading

0 comments on commit b42fb5c

Please sign in to comment.