velocityzen/fp-swift
_This is not a full fledged package for functional programming in Swift. This will have to wait until Higher Kinded Types are part of the language. However this will make it easier to write functional code using built-in Swift Result and Optional types._
Contents
- Pipe Operators - Result Extensions - Flatten Results - Array Extensions - AsyncSequence Result Processing - Ordered Concurrent Mapping - AsyncStream Extensions - Optional Extensions
- Result Extensions API - Flatten Functions API - Array Extensions API - AsyncSequence Extensions API - AsyncStream Extensions API - Optional Extensions API
Requirements
- Swift 6.2+
- macOS 15+ / iOS 18+
Installation
Add the package to your Package.swift:
dependencies: [
.package(url: "https://github.com/velocityzen/fp-swift.git", from: "1.7.0")
]Then import it:
import FPFeatures
### Pipe Operators
Forward pipe operators for function composition:
```swift
// Basic pipe: pass value to function
let result = 5 |> double |> toString // "10"
// Pipe into second argument
let result = value |>> (function, firstArg)
// Pipe into third argument
let result = value |>>> (function, firstArg, secondArg)
// Flow operator: create function from another function
let transform = |> double // (Int) -> Int
```
### Result Extensions
#### Do Notation for Composing Results
```swift
func createOrder(userId: Int, itemId: Int) -> Result<Order, AppError> {
ResultDo<AppError>()
.bind { fetchUser(id: userId) }
.bind { user in fetchItem(id: itemId) }
.let { user, item in item.price * user.discountRate }
.bind { user, item, price in
validateOrder(user: user, item: item, price: price)
}
.map { user, item, price, validation in
Order(user: user, item: item, price: price)
}
}
// Async variant with mixed sync/async steps
func createOrderAsync(userId: Int, itemId: Int) async -> Result<Order, AppError> {
await ResultDo<AppError>()
.bindAsync { await fetchUser(id: userId) }
.bindAsync { user in await fetchItem(id: itemId) }
.let { user, item in item.price * user.discountRate }
.bindAsync { user, item, price in
await validateOrder(user: user, item: item, price: price)
}
.map { user, item, price, validation in
Order(user: user, item: item, price: price)
}
}
```
#### Chaining Async Operations
```swift
func processUser(id: Int) async -> Result<ProcessedUser, Error> {
await Result.fromAsync { try await api.fetchUser(id: id) }
.tapAsync { user in await analytics.track(.userFetched(user)) }
.mapAsync { user in await enrichUserData(user) }
.flatMapAsync { user in await validateUser(user) }
.tapError { error in logger.error("Failed: \(error)") }
}
```
#### Async Map Operations
```swift
let result: Result<Int, Error> = .success(42)
// Transform success value asynchronously
let mapped = await result.mapAsync { value in
await fetchData(for: value)
}
// FlatMap for chaining Result-returning async operations
let chained = await result.flatMapAsync { value in
await validateAndTransform(value) // Returns Result<T, Error>
}
// Create Result from async throwing operation
let result = await Result.fromAsync {
try await networkCall()
}
// Create Result from Task
let task = Task { try await networkCall() }
let result = await Result.fromTask(task)
// Or with closure syntax
let result = await Result.fromTask {
Task { try await networkCall() }
}
// Also works with Tasks returning Results
let task = Task { await someResultOperation() }
let result: Result<Value, Error> = await Result.fromTask(task)
```
#### Map to Constant Value
Replace the success value with a constant or discard it entirely:
```swift
let result: Result<Int, AppError> = .success(42)
// Map to a specific constant
let mapped = result.as("done") // .success("done")
// Map to Void (discard the success value)
let unit = result.asUnit() // .success(())
// Useful in chains where you only care about success/failure
fetchUser(id: 1)
.tap { user in saveToCache(user) }
.asUnit() // Result<Void, AppError>
```
#### Tap for Side Effects
Perform side effects while keeping the Result chain flowing:
```swift
someOperation()
.tap { value in saveToCache(value) }
.tapError { error in logError(error) }
.map { value in transform(value) }
// Async variants
await result
.tapAsync { value in await sendAnalytics(value) }
.tapErrorAsync { error in await reportError(error) }
```
Tap variants:
- `.tap()` - Sync side effect on success
- `.tapAsync()` - Async side effect on success
- `.tapError()` - Sync side effect on failure
- `.tapErrorAsync()` - Async side effect on failure
#### Alt — Recover with an Alternative
Mirrors fp-ts `Either.alt`. If `self` is a success, keep it; otherwise return the lazily-evaluated alternative. The alternative may itself succeed (recovery) or fail (in which case its failure replaces the original):
```swift
fetchUser(id: 1)
.alt { fetchUserFromCache(id: 1) }
.alt { .success(.guest) }
// Async variant
await fetchUser(id: 1)
.altAsync { await fetchUserFromCache(id: 1) }
```
#### GetOrElse — Unwrap with a Fallback
Mirrors fp-ts `Either.getOrElse`. Returns the success value, or computes/returns a fallback when the result is a failure. Unlike `alt`, this returns the unwrapped `Success` rather than another `Result`:
```swift
// Closure-based — receives the error
let count = parse(input).getOrElse { _ in 0 }
// Constant default (lazily evaluated)
let count = parse(input).getOrElse(0)
// Async variant — closure receives the error
let user = await fetchUser(id: 1).getOrElseAsync { _ in
await loadGuestUser()
}
// Async variant — lazily-evaluated async default
let user = await fetchUser(id: 1).getOrElseAsync(await loadGuestUser())
```
- Throwing variants convert `Failure` type to `Error`
#### Match
Branch on a Result without switching manually:
```swift
let message = result.match(
{ "value: \($0)" },
{ "error: \($0)" }
)
let fallback = result.match("ok", "error")
let mixed = result.match(
{ "value: \($0)" },
"error"
)
let asyncMessage = await result.matchAsync(
{ value in
await Task.yield()
return "value: \(value)"
},
{ error in
await Task.yield()
return "error: \(error)"
}
)
let asyncMixed = await result.matchAsync(
"ok",
{ error in
await Task.yield()
return "error: \(error)"
}
)
// Use match for side effects without capturing the result
result.match(
{ value in print("Success: \(value)") },
{ error in print("Error: \(error)") }
)
```
#### From Optional
Convert optionals to Results:
```swift
// With static error
let result = Result<User, AppError>.fromOptional(user, error: .notFound)
// With lazy error (only evaluated if nil)
let result = Result<User, AppError>.fromOptional(user) {
.notFound(id: userId)
}
```
#### To Bool
Convert Result to boolean for simple success/failure checks:
```swift
let result: Result<User, AppError> = fetchUser(id: 1)
if result.toBool {
print("User fetched successfully")
}
// Or in ternary expressions
let message = result.toBool ? "ok" : "error"
```
### Flatten Results
Combine multiple Results into a single Result with a tuple of values:
```swift
let userResult: Result<User, AppError> = fetchUser(id: 1)
let profileResult: Result<Profile, AppError> = fetchProfile(id: 1)
let settingsResult: Result<Settings, AppError> = fetchSettings(id: 1)
// Sync flatten - combine already-computed Results
let combined = flatten(userResult, profileResult, settingsResult)
// Result<(User, Profile, Settings), AppError>
// Use map to create named tuple for easier access
let namedResult = flatten(userResult, profileResult)
.map { (user: $0, profile: $1) }
// Result<(user: User, profile: Profile), AppError>
if case .success(let data) = namedResult {
print(data.user.name)
print(data.profile.bio)
}
```
#### Async Flatten with Parallel Execution
Run multiple async operations in parallel and combine their results:
```swift
func loadDashboard(userId: Int) async -> Result<Dashboard, AppError> {
// All three operations run in parallel
let result = await flattenAsync(
await fetchUser(id: userId),
await fetchNotifications(for: userId),
await fetchRecommendations(for: userId)
)
// Result<(User, [Notification], [Recommendation]), AppError>
return result.map { user, notifications, recommendations in
Dashboard(user: user, notifications: notifications, recommendations: recommendations)
}
}
```
### Array Extensions
#### Traverse
Apply a Result-returning transform to each element, short-circuiting on first failure:
```swift
let userIds = [1, 2, 3]
// Sync traverse
let result = userIds.traverse { id -> Result<User, AppError> in
fetchUser(id: id)
}
// Returns .success([User]) or .failure on first error
// Async traverse
let result = await userIds.traverseAsync { id in
await fetchUserAsync(id: id)
}
```
#### Async Mapping
Asynchronous versions of `map`, `flatMap`, and `compactMap`:
```swift
let items = [1, 2, 3, 4, 5]
let mapped = await items.mapAsync { item in
"v\(item)"
}
let flattened = await items.flatMapAsync { item in
[item, item * 10]
}
let compacted = await items.compactMapAsync { item -> String? in
await processItem(item) // Returns nil for items to filter out
}
enum ParseError: Error {
case invalid
}
let resultMapped = await items.mapAsync { item -> Result<String, ParseError> in
.success("item-\(item)")
}
let resultFlattened = await items.flatMapAsync { item -> Result<[Int], ParseError> in
.success([item, item + 100])
}
let resultCompacted = await items.compactMapAsync { item -> Result<String?, ParseError> in
.success(item.isMultiple(of: 2) ? "even-\(item)" : nil)
}
```
### AsyncSequence Result Processing
Process streams of Results with familiar functional operations:
```swift
let stream = AsyncStream<Result<Int, AppError>> { continuation in
continuation.success(1)
continuation.success(2)
continuation.failure(.invalid)
continuation.success(3)
continuation.finish()
}
// Filter to just success values
for await value in stream.successes() {
print(value) // 1, 2, 3
}
// Transform, tap, and chain
for await result in stream
.tap { value in logger.info("got \(value)") }
.tapError { error in logger.error("\(error)") }
.mapAsync { value in await enrich(value) }
.flatMap { value in validate(value) }
{
// ...
}
```
### Ordered Concurrent Mapping
Map elements through an async transform in parallel while preserving source order. Each transform runs in its own `Task`, so wall-clock time is bounded by the slowest element rather than the sum of all transforms — but emission still follows source arrival order. Useful when an upstream provider streams items that should be processed concurrently but consumed in order (e.g. SSE image references that need to be fetched in parallel and rendered in order):
```swift
for await image in references.mapAsyncKeepOrder({ ref in
await downloader.fetch(ref)
}) {
render(image)
}
```
When the source is a stream of `Result`, an overload transforms only the success values and passes failures through unchanged — preserving order across both:
```swift
for await result in events.mapAsyncKeepOrder({ event in
await enrich(event)
}) {
handle(result) // Result<EnrichedEvent, MyError>
}
```
### AsyncStream Extensions
Create single-element Result streams or use convenience methods on continuations:
```swift
// Static factories — single-element streams
let success: AsyncStream<Result<Int, MyError>> = .success(42)
let failure: AsyncStream<Result<Int, MyError>> = .failure(.someError)
// Continuation helpers
let stream = AsyncStream<Result<Int, MyError>> { continuation in
continuation.success(1)
continuation.success(2)
continuation.failure(.someError)
continuation.finish()
}
// Or finish with a final value
let stream = AsyncStream<Result<String, MyError>> { continuation in
continuation.success("processing...")
continuation.finishWithSuccess("done") // Yields and finishes
}
// Finish with error
let stream = AsyncStream<Result<Data, NetworkError>> { continuation in
continuation.finishWithFailure(.connectionLost) // Yields error and finishes
}
```
### Optional Extensions
#### Match
```swift
let optional: String? = "hello"
let message = optional.match(
{ "got: \($0)" },
"nothing"
)
// "got: hello"
let missing: String? = nil
missing.match(
{ value in print(value) },
()
)
// Does nothing
```
#### Async Mapping
```swift
let optional: Int? = 42
let mapped = await optional.mapAsync { value in
await fetchDetails(for: value)
}
// Returns nil if optional was nil, otherwise the transformed value
let flatMapped = await optional.flatMapAsync { value -> String? in
value > 0 ? "id-\(value)" : nil
}
```
#### OrElse
```swift
let optional: Int? = nil
let fallback = optional.orElse(99) // Returns 99 when nil, nil when has value
```Result Extensions API
### Do Notation
Monadic do-notation for composing multiple Result operations with an accumulating context (like fp-ts `Do` / `bind` / `let`):
```swift
// ResultDo starts the chain, bind adds Result values, let adds pure values
let result = ResultDo<MyError>()
.bind { getUser() } // Result<User, MyError>
.bind { user in getProfile(user) } // Result<(User, Profile), MyError>
.let { _, profile in profile.name } // Result<(User, Profile, String), MyError>
.map { user, _, name in "\(user.id): \(name)" } // Result<String, MyError>
// Short-circuits on the first failure
let result = ResultDo<MyError>()
.bind { getUser() } // .failure(.notFound) → stops here
.bind { user in getProfile(user) } // never called
.map { user, profile in profile } // never called
// result == .failure(.notFound)
```
**API:**
```swift
// Start the chain
ResultDo<Failure>()
// Bind: add a Result value, accumulates into a growing tuple
func bind<A>(_ f: () -> Result<A, Failure>) -> Result<A, Failure>
func bind<B>(_ f: (A) -> Result<B, Failure>) -> Result<(A, B), Failure>
func bind<C>(_ f: (A, B) -> Result<C, Failure>) -> Result<(A, B, C), Failure>
// ... up to 10 accumulated values
// Let: add a pure (non-Result) value
func `let`<A>(_ f: () -> A) -> Result<A, Failure>
func `let`<B>(_ f: (A) -> B) -> Result<(A, B), Failure>
func `let`<C>(_ f: (A, B) -> C) -> Result<(A, B, C), Failure>
// ... up to 10 accumulated values
// Async variants: bindAsync / letAsync
func bindAsync<A>(_ f: () async -> Result<A, Failure>) async -> Result<A, Failure>
func bindAsync<B>(_ f: (A) async -> Result<B, Failure>) async -> Result<(A, B), Failure>
// ... up to 10 accumulated values
func letAsync<A>(_ f: () async -> A) async -> Result<A, Failure>
func letAsync<B>(_ f: (A) async -> B) async -> Result<(A, B), Failure>
// ... up to 10 accumulated values
```
Sync and async can be freely mixed in the same chain:
```swift
let result = await ResultDo<MyError>()
.bind { getCachedUser() } // sync
.bindAsync { user in await fetchProfile(user) } // async
.let { user, profile in profile.name } // sync
.mapAsync { user, profile, name in // async
await formatDisplay(user, name)
}
```
### Map & FlatMap
```swift
// Non-throwing
func mapAsync<T>(_ transform: (Success) async -> T) async -> Result<T, Failure>
func mapFailureAsync<E: Error>(_ transform: (Failure) async -> E) async -> Result<Success, E>
func flatMapAsync<T>(_ transform: (Success) async -> Result<T, Failure>) async -> Result<T, Failure>
// Throwing (requires Failure == Error)
func mapAsync<T>(_ transform: (Success) async throws -> T) async -> Result<T, Error>
// Map to constant value
func `as`<T>(_ value: T) -> Result<T, Failure>
func asUnit() -> Result<Void, Failure>
```
### Alt
```swift
// Lazily provides an alternative on failure (analogue of fp-ts Either.alt)
func alt(_ alternative: () -> Result<Success, Failure>) -> Result<Success, Failure>
func altAsync(_ alternative: () async -> Result<Success, Failure>) async -> Result<Success, Failure>
```
### GetOrElse
```swift
// Unwrap or fall back (analogue of fp-ts Either.getOrElse)
func getOrElse(_ onFailure: (Failure) -> Success) -> Success
func getOrElse(_ defaultValue: @autoclosure () -> Success) -> Success
func getOrElseAsync(_ onFailure: (Failure) async -> Success) async -> Success
func getOrElseAsync(_ defaultValue: @autoclosure @escaping () async -> Success) async -> Success
```
### Match
All match variants are marked `@discardableResult`, so you can use them both for transforming values and for side effects without assigning the result.
```swift
@discardableResult func match<T>(_ onSuccess: (Success) -> T, _ onFailure: (Failure) -> T) -> T
@discardableResult func match<T>(_ onSuccess: (Success) -> T, _ failure: @autoclosure () -> T) -> T
@discardableResult func match<T>(_ success: @autoclosure () -> T, _ onFailure: (Failure) -> T) -> T
@discardableResult func match<T>(_ success: @autoclosure () -> T, _ failure: @autoclosure () -> T) -> T
@discardableResult func matchAsync<T>(_ onSuccess: (Success) async -> T, _ onFailure: (Failure) async -> T) async -> T
@discardableResult func matchAsync<T>(_ onSuccess: (Success) async -> T, _ failure: @autoclosure () -> T) async -> T
@discardableResult func matchAsync<T>(_ success: @autoclosure () -> T, _ onFailure: (Failure) async -> T) async -> T
```
### From Async
```swift
// Throwing (requires Failure == Error)
static func fromAsync(_ operation: () async throws -> Success) async -> Result<Success, Error>
```
### From Task
```swift
// Throwing Task (requires Failure == Error)
static func fromTask(_ task: Task<Success, Error>) async -> Result<Success, Error>
static func fromTask(_ task: () -> Task<Success, Error>) async -> Result<Success, Error>
// Non-throwing Task (requires Failure == Never)
static func fromTask(_ task: Task<Success, Never>) async -> Result<Success, Never>
static func fromTask(_ task: () -> Task<Success, Never>) async -> Result<Success, Never>
// Task returning Result (requires Failure == Error)
static func fromTask<S>(_ task: Task<Result<S, Error>, Never>) async -> Result<S, Error>
static func fromTask<S>(_ task: () -> Task<Result<S, Error>, Never>) async -> Result<S, Error>
```
### From Optional
```swift
static func fromOptional(_ optional: Success?, error: Failure) -> Result<Success, Failure>
static func fromOptional(_ optional: Success?, onError: () -> Failure) -> Result<Success, Failure>
static func fromOptional(error: Failure) -> (Success?) -> Result<Success, Failure>
static func fromOptional(onError: () -> Failure) -> (Success?) -> Result<Success, Failure>
```
### To Bool
```swift
var toBool: Bool // true for success, false for failure
```
### Tap (Side Effects)
```swift
// Non-throwing
func tap(_ action: (Success) -> Void) -> Result<Success, Failure>
func tap<T>(_ action: (Success) -> T) -> Result<Success, Failure>
func tap<E>(_ action: (Success) -> Result<Void, E>) -> Result<Success, E>
func tap<T, E>(_ action: (Success) -> Result<T, E>) -> Result<Success, E>
// Throwing
func tap(_ action: (Success) throws -> Void) -> Result<Success, Error>
func tap<T>(_ action: (Success) throws -> T) -> Result<Success, Error>
// Async non-throwing
func tapAsync(_ action: (Success) async -> Void) async -> Result<Success, Failure>
func tapAsync<T>(_ action: (Success) async -> T) async -> Result<Success, Failure>
func tapAsync<E>(_ action: (Success) async -> Result<Void, E>) async -> Result<Success, E>
func tapAsync<T, E>(_ action: (Success) async -> Result<T, E>) async -> Result<Success, E>
// Async throwing
func tapAsync(_ action: (Success) async throws -> Void) async -> Result<Success, Error>
func tapAsync<T>(_ action: (Success) async throws -> T) async -> Result<Success, Error>
```
### TapError (Side Effects on Failure)
```swift
// Non-throwing
func tapError(_ action: (Failure) -> Void) -> Result<Success, Failure>
func tapError<T>(_ action: (Failure) -> T) -> Result<Success, Failure>
func tapError<T, E>(_ action: (Failure) -> Result<T, E>) -> Result<Success, E>
// Throwing
func tapError(_ action: (Failure) throws -> Void) -> Result<Success, Error>
func tapError<T>(_ action: (Failure) throws -> T) -> Result<Success, Error>
// Async non-throwing
func tapErrorAsync(_ action: (Failure) async -> Void) async -> Result<Success, Failure>
func tapErrorAsync<T>(_ action: (Failure) async -> T) async -> Result<Success, Failure>
func tapErrorAsync<T, E>(_ action: (Failure) async -> Result<T, E>) async -> Result<Success, E>
// Async throwing
func tapErrorAsync(_ action: (Failure) async throws -> Void) async -> Result<Success, Error>
func tapErrorAsync<T>(_ action: (Failure) async throws -> T) async -> Result<Success, Error>
```Flatten Functions API
Combine multiple Results into a single Result containing a tuple of all success values. If any Result fails, returns the first failure.
Sync Flatten
// Supports 2-10 arguments
func flatten<A, B, E: Error>(_ a: Result<A, E>, _ b: Result<B, E>) -> Result<(A, B), E>
func flatten<A, B, C, E: Error>(_ a: Result<A, E>, _ b: Result<B, E>, _ c: Result<C, E>) -> Result<(A, B, C), E>
// ... up to 10 argumentsAsync Flatten (Parallel Execution)
// Supports 2-10 arguments, runs all operations in parallel
func flattenAsync<A: Sendable, B: Sendable, E: Error>(
_ a: @Sendable @autoclosure @escaping () async -> Result<A, E>,
_ b: @Sendable @autoclosure @escaping () async -> Result<B, E>
) async -> Result<(A, B), E>
// ... up to 10 argumentsArray Extensions API
Traverse
func traverse<Success>(_ transform: (Element) -> Success) -> Result<[Success], Never>
func traverse<Success, Failure>(_ transform: (Element) -> Result<Success, Failure>) -> Result<[Success], Failure>
func traverseAsync<Success>(_ transform: (Element) async -> Success) async -> Result<[Success], Never>
func traverseAsync<Success, Failure>(_ transform: (Element) async -> Result<Success, Failure>) async -> Result<[Success], Failure>Separate
func separate<Success, Failure>() -> (successes: [Success], failures: [Failure])
where Element == Result<Success, Failure>Async Mapping
// mapAsync
func mapAsync<T>(_ transform: (Element) async -> T) async -> [T]
func mapAsync<T, Failure: Error>(
_ transform: (Element) async -> Result<T, Failure>
) async -> Result<[T], Failure>
// flatMapAsync
func flatMapAsync<S: Sequence>(
_ transform: (Element) async -> S
) async -> [S.Element]
func flatMapAsync<S: Sequence, Failure: Error>(
_ transform: (Element) async -> Result<S, Failure>
) async -> Result<[S.Element], Failure>
// compactMapAsync
func compactMapAsync<T>(_ transform: (Element) async -> T?) async -> [T]
func compactMapAsync<T, Failure: Error>(
_ transform: (Element) async -> Result<T?, Failure>
) async -> Result<[T], Failure>AsyncSequence Extensions API
Extensions for any AsyncSequence where Element == Result<Success, Failure>:
Filter
func successes() -> AsyncCompactMapSequence // unwraps success values
func failures() -> AsyncCompactMapSequence // unwraps failure errorsMap & FlatMap
// Sync
func map<T>(_ transform: (Success) -> T) -> AsyncMapSequence<Self, Result<T, Failure>>
func mapFailure<E>(_ transform: (Failure) -> E) -> AsyncMapSequence<Self, Result<Success, E>>
func flatMap<T>(_ transform: (Success) -> Result<T, Failure>) -> AsyncMapSequence<Self, Result<T, Failure>>
// Async
func mapAsync<T>(_ transform: (Success) async -> T) -> AsyncMapSequence<Self, Result<T, Failure>>
func mapFailureAsync<E>(_ transform: (Failure) async -> E) -> AsyncMapSequence<Self, Result<Success, E>>
func flatMapAsync<T>(_ transform: (Success) async -> Result<T, Failure>) -> AsyncMapSequence<Self, Result<T, Failure>>Tap
// Sync
func tap(_ action: (Success) -> Void) -> AsyncMapSequence<Self, Result<Success, Failure>>
func tapError(_ action: (Failure) -> Void) -> AsyncMapSequence<Self, Result<Success, Failure>>
// Async
func tapAsync(_ action: (Success) async -> Void) -> AsyncMapSequence<Self, Result<Success, Failure>>
func tapErrorAsync(_ action: (Failure) async -> Void) -> AsyncMapSequence<Self, Result<Success, Failure>>Ordered Concurrent Mapping
Element transforms run in parallel; output preserves source arrival order.
// General form
func mapAsyncKeepOrder<T: Sendable>(
_ transform: @Sendable @escaping (Element) async -> T
) -> AsyncStream<T>
where Self: Sendable, Failure == Never, Element: Sendable
// Result overload — transforms successes, passes failures through
func mapAsyncKeepOrder<Success: Sendable, E: Error, T: Sendable>(
_ transform: @Sendable @escaping (Success) async -> T
) -> AsyncStream<Result<T, E>>
where Self: Sendable, Failure == Never, Element == Result<Success, E>AsyncStream Extensions API
Static Factories
Create single-element Result streams:
static func success<Success, Failure>(_ value: Success) -> AsyncStream<Result<Success, Failure>>
static func failure<Success, Failure>(_ error: Failure) -> AsyncStream<Result<Success, Failure>>Continuation Extensions
Extensions for AsyncStream.Continuation when the element type is Result<Success, Failure>:
// Yield success/failure values
func success<Success, Failure>(_ value: Success) -> YieldResult
func failure<Success, Failure>(_ error: Failure) -> YieldResult
// Yield and finish the stream
func finishWithSuccess<Success, Failure>(_ value: Success)
func finishWithFailure<Success, Failure>(_ error: Failure)Optional Extensions API
Match
@discardableResult func match<T>(_ onSome: (Wrapped) -> T, _ onNone: @autoclosure () -> T) -> T
@discardableResult func matchAsync<T>(_ onSome: (Wrapped) async -> T, _ onNone: @autoclosure () -> T) async -> TAsync Mapping
func mapAsync<T>(_ transform: (Wrapped) async -> T) async -> T?
func flatMapAsync<T>(_ transform: (Wrapped) async -> T?) async -> T?OrElse
func orElse<T>(_ defaultValue: T) -> T?License
MIT
Package Metadata
Repository: velocityzen/fp-swift
Default branch: main
README: README.md