Swift package
Nominal Swift functions that compose while preserving effects and isolation
Raw closures are excellent for one-off work, but overloads become fragile when a pipeline must account for async, throws, sendability, or actor isolation. Nominal wrappers make those effects part of the composed value’s type.
let isNotZero = SyncFunc<Int, Bool> { $0 != 0 }
let describe = SyncFunc<Bool, String> {
$0 ? "true" : "false"
}
let description = describe <<< isNotZero
let result = description(10)
Choose the surface that reads naturally in the target: operators, .compose(...) methods, compose(...), or forward pipe(...). All wrappers support both run(with:) and callAsFunction.
Composition upgrades monotonically: sync plus async becomes async; nonthrowing plus throwing becomes throwing; compatible sendable and main-actor functions preserve main-actor isolation. Different typed failures become Either<Left, Right> instead of being silently erased.
let load = SendableAsyncThrowingFunc<Int, Bool, LoadError> {
try await client.loadFlag($0)
}
let render = SendableSyncFunc<Bool, String> {
$0 ? "Enabled" : "Disabled"
}
let loadDescription = render <<< load
Swift 6.1 package traits let each target opt into only the surfaces it uses: NominalTypes, Operators, Methods, Functions, and Currying.
| Effect shape | Wrapper family |
|---|---|
| Synchronous | SyncFunc / SendableSyncFunc / MainActorSyncFunc |
| Throwing | SyncThrowingFunc<Input, Output, Failure> |
| Asynchronous | AsyncFunc and sendable or main-actor variants |
| Async throwing | AsyncThrowingFunc<Input, Output, Failure> |
The same pipeline can be spelled with <<<, .compose(...), compose(...), or forward pipe(...). All nominal wrappers support run(with:) and callAsFunction; enable Currying only when curry, uncurry, or flip makes the function shape clearer.