swift-primitives/swift-either-primitives
Either<Left, Right> — a generic sum type holding exactly one of two alternatives. The categorical coproduct: where a binary product holds both values, Either holds one. ~Copyable & ~Escapable at the type level, with conditional Copyable, Escapable, Sendable, `Bitwis
Quick Start
import Either_Primitives
let success: Either<String, Int> = .right(42)
let failure: Either<String, Int> = .left("not found")
let doubled = success.map(right: { $0 * 2 }) // .right(84)
let flipped = success.swapped() // Either<Int, String>.left(42)Never on either side eliminates the case unconditionally:
let certain: Either<Never, Int> = .right(10)
print(certain.value) // 10 — left side is uninhabitedmap(left:right:) transforms both arms in one pass:
let labelled: Either<String, Int> = .right(7)
let widened = labelled.map(
left: { "error: \($0)" },
right: { Double($0) }
) // Either<String, Double>.right(7.0)fold(left:right:) collapses an Either into a single value by handling both cases — the universal property of the coproduct:
let message = either.fold(
left: { walkError in describe(walkError) },
right: { userError in describe(userError) }
)Both arms may be ~Copyable and ~Escapable. Non-closure operations — swapped(), the value(of:) free function for Never-eliminated arms, and the institute-protocol conformances (Equation.Protocol / Hash.Protocol / Comparison.Protocol) — admit ~Escapable arms today. Closure-bearing methods (map(left:), map(right:), map(left:right:), flatMap(left:), flatMap(right:), fold(left:right:), equal-arm map { f } / flatMap { f }) admit ~Escapable on the un-transformed arm only; both arms ~Escapable through a closure is currently blocked by Swift's lifetime-from-closure-result limitation. The .left / .right peek accessors admit Copyable & ~Escapable arms with lifetime tied to the borrowed receiver; broader peek accessors for ~Copyable arms are deferred pending stdlib Borrow<T>.
Installation
dependencies: [
.package(url: "https://github.com/swift-primitives/swift-either-primitives.git", branch: "main")
].target(
name: "App",
dependencies: [
.product(name: "Either Primitives", package: "swift-either-primitives"),
]
)Requires Swift 6.3.1 and macOS 26 / iOS 26 / tvOS 26 / watchOS 26 / visionOS 26 (or the matching Linux / Windows toolchain).
Architecture
One library product, one target.
| Product | Target | Contents | |---------|--------|----------| | Either Primitives | Sources/Either Primitives/ | Either<Left, Right> + map(left:) / map(right:) / map(left:right:) / flatMap(right:) / flatMap(left:) / fold(left:right:) / swapped() + .left / .right accessors + Never-elimination value accessors. |
The conditional conformance ladder mirrors stdlib Result.swift's triple-extension pattern. Conditional Equation.Protocol, Hash.Protocol, and Comparison.Protocol conformances admit ~Copyable arms — Either<NCResource, NCResource> is comparable, hashable, and equation-conforming via the borrowing operators when arms conform.
Dependencies (path-resolved at development time): swift-equation-primitives, swift-hash-primitives, swift-comparison-primitives. Foundation-free.
Platform Support
| Platform | Status | |----------|--------| | macOS 26 | Full support | | iOS / tvOS / watchOS / visionOS | Full support | | Linux | Full support (Swift 6.3 release + Static musl + Android SDK + Embedded Wasm SDK) | | Windows | Full support (Swift 6.3) | | Swift Embedded | Full support (Swift 6.4-dev nightly Embedded build); Codable is #if !hasFeature(Embedded) gated |
License
Apache 2.0. See LICENSE.md.
Package Metadata
Repository: swift-primitives/swift-either-primitives
Default branch: main
README: README.md