Skip to content

Library for writing Swift code with a slight touch of functional programming.

License

Notifications You must be signed in to change notification settings

VAndrJ/Swiftional

Repository files navigation

Swiftional

StandWithUkraine Support Ukraine

License Platform Language

Version SPM  Swiftional Coverage Status

Swiftional introduces some functional primitives that complement the Swift standard library.

Created for writing Swift code with a slight touch of functional programming.

1.x.x – Swift 5.8

2.x.x – Swift 5.9

Functions

curry

Converts an uncurried function to a curried function.

Example:

(A, B) -> R
becomes
(A) -> (B) -> R
uncurry

Converts a curried function into an uncurried function.

Example:

(A) -> (B) -> R
becomes
(A, B) -> R
partial

Partial application. Applies an argument to a function.

Example:

(A, B) -> R
with applied first argument becomes
(B) -> R
identity

Identity combinator function. Returns the input without changing it.

constant

The constant combinator function. Ignores the function arguments and always returns the provided value.

flip

Flips the arguments of a function.

Example:

(A, B) -> R
becomes
(B, A) -> R
with

It calls the specified closure with the given attribute as its receiver and returns its result.

ignored

Ignores the function return and always returns Void.

weakify

Weakifying function.

Example:

// Instead of this:
someObject.onActionClosure = otherObject.someFunc // `otherObject` captured by strong reference
// Use operator:
someObject.onActionClosure = weakify(otherObject) { $0.someFunc() } // `otherObject` is weakified, not captured by strong reference
memoize

Memoization function. Memoize wrapper intercepts calls you send to the function and attempts to reply with results from its internal cache. If it fails to find a cached result, it calls the work function and records the result of the computation in memory. Subsequent calls to the function with the same arguments can then be satisfied by fetching the result from memory, avoiding redundant computations. Memoization is one of the oldest and simplest tricks in computer science, trading memory for CPU cycles.

Example:

let memoizedSomeFunc = memoize(f: someFunc(_:))
print(memoizedSomeFunc(2))
print(memoizedSomeFunc(3))
print(memoizedSomeFunc(2)) // result fetched from memory
rmemoize

Standard memoization is not very good at memoizing recursive functions. Here is the recursive memoization function. It is represented as a primitive recursive function, where the memoization is done at each step of the recursion.

Example:

let memoizedFibonacci = rmemoize { fibonacci, n in n < 2 ? 1 : fibonacci(n - 1) + fibonacci(n - 2) }
print(memoizedFibonacci(42))

Extensions

Bool
  • fold Case analysis for the Bool type. Applies the provided closures based on the value.

  • foldRun Runs the provided closures based on the content of this value.

  • foldEither Case analysis for the Bool type. Applies the provided closures based on the value and return Either.

Optional
  • fold Case analysis for the Optional type. Applies the provided closures based on the content of this Optional value.

Protocols

Applyable
  • apply Calls the specified closure with the Self value as its receiver and returns the Self value.

  • applied Calls the specified closure with the Self value as its receiver and returns a copy of the Self value.

Operators

>>>

Composes functions and returns a function that is the result of applying g to the output of f.

<<<

Composes functions and returns a function that is the result of applying g to the output of f.

|>

Pipe forward. Applies an argument to a function.

Example. This:

let result = h(parameter: g(parameter: f(parameter: a)))

Can also be written as:

let result = a |> f |> g |> h
<|

Pipe backward. Applies an argument to a function.

Example. This:

let result = h(parameter: g(parameter: f(parameter: a)))

Can also be written as:

let result = h <| g <| f <| a
|>>

Applies a function to an argument and returns a callable function.

Example. This:

let result = { a in f(parameter: a) }

Can also be written as:

let result = a |>> f
<<|

Applies a function to an argument and returns a callable function.

Example. This:

let result = { a in f(parameter: a) }

Can also be written as:

let result = f <<| a
~~>

Asynchronous function composition

>=>

Effectful function composition

?>

Weakifying function.

Example:

// Instead of this:
someObject.onActionClosure = otherObject.someFunc // `otherObject` captured by strong reference
// Use operator:
someObject.onActionClosure = otherObject ?> { $0.someFunc() } // `otherObject` is weakified, not captured by strong reference
?>>

Weakifying function.

Example:

// Instead of this:
someObject.onActionClosure = otherObject.someFunc // `otherObject` captured by strong reference
// Use operator:
someObject.onActionClosure = otherObject ?>> { $0.someFunc } // `otherObject` is weakified, not captured by strong reference

Types

Either

The type Either represents a value of one of these types, but not both: .left(Left) or .right(Right).

The Either type is shifted to the right by convention. That is, the .left constructor is usually used to hold errors or secondary data, while .right is used to store a "correct", primary value - one that can be worked on further.

Wordplay: "Right" also means "Correct".

Author

Volodymyr Andriienko, [email protected]

License

VANavigator is available under the MIT license. See the LICENSE file for more info.