Improving our functions with Bow

Share This Post

**Bow is a library for Typed Functional Programming in Swift**

But first of all…

What is Functional Programming?

Functional Programming is a programming paradigm – a style of building the structure and elements of computer programs – that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data. -Wikipedia

TL;DR: *Functional Programming is programming with functions.*

Functions must be:

-Total: There is an output for every input
– Deterministic: For a given input, the function always return the same output.
– Pure: The evaluation of the functions does not cause other effects besides computing the output.

But… All state is bad?

No, hidden, implicit state is bad.

Functional programming do not eliminate state, it just make it visible and explicit.

func add(x: Int, y: Int) -> Int {
    return x + y
}

For example this functions is **total**, **deterministic** and **pure**. Always will return the same output for the same input, will return always an output and does not fire any side effects.


func divide(x: Int, y: Int) -> Int {
    guard y != 0 else { fatalError("Division by 0") }
    return x / y
}

This function is **deterministic** and **pure** but not **total**. Is a **partial** functions because if the seconds argument is 0, theres is no way yo provide a valid output.


func dice(withSides sides: UInt32) -> UInt32 {
    return 1 + arc4random_uniform(sides)
}

Two invocations with the same input will (most likely) yield different values, as the output is randomized. This means the function is **non-deterministic.**


func save(data: String) -> Bool {
    print("Saving data: \(data)")
    return true
}

That functions is **total** and **deterministic** but is not pure because on every execution will do something more besides computing the output. It prints a message in that example. That makes this function **impure.**

What we can we do?

Working with impure functions

Real world software rarely has functions as nicely written as the **`add`** function above. We usually have to deal with partial, non-deterministic and/or impure functions, since software has to deal with errors and perform side effects in order to be useful. Besides presenting issues breaking referential transparency, function composition cannot be done in these circumstances. How can we deal with such situations? Bow provides numerous data types that can be used to model different effects in our functions. Using those data types, such as **`Option`**, **`Either`** or **`IO`** can help us transform partial, non-deterministic or impure functions into total, deterministic and pure ones. Once we have them, we can use combinators that will help us compose those functions nicely. We will use bow data types to transform some partial, impure and non-deterministic. **Working with the previous function:**


func divide(x: Int, y: Int) -> Int {
    guard y != 0 else { fatalError("Division by 0") }
    return x / y
}

We will use a data type that bow provides. **Option**


func divideOption(x : Int, y : Int) -> Option {
    guard y != 0 else { return Option.none() }
    return Option.some(x / y)
}

Now, divideOption is able to return a value for each possible input; i.e., it is a total function. `Option` is similar to Swift Optional. That means Option and Optional are isomorphic: there is a pair of functions (namely toOption and fromOption) that, when composed, their result is the identity function. Also we can rewrite the divide function using `Either`.


func divideEither(x : Int, y : Int) -> Either<DivideError, Int> {
    guard y != 0 else { return Either.left(.divisionByZero) }
    return Either.right(x / y)
}

With Either allows us to be more explicit about the error in the return type, helping the caller to be prepared to deal with the possible outputs it may receive. Nonetheless, **the left type does not need to be an error.** That’s the main different between swift data type **Result vs Either.** With the Result type we have the method to migrate it to an either or options importing **BowResult.**


import BowResult
let result = Result<Int, DivideError>(value: 2)
let eitherFromResult = result.toEither()
let optionFromResult = result.toOption()

Effects

So far we have seen some data types that Bow provides in order to help us convert our partial functions into total ones. However, in many cases we need to perform effects, which make our functions to be impure. For these cases, Bow provides the `IO` data type, in order to encapsulate effects.


func fetchSomething() -> (String) {
		//Server call
		return "{ \"MyData\ }"
}

> The IO type encapsulate an effect of type A, but does not execute it


func fetchSomething() -> IO {
		return IO.invoke {
				return "{ \"MyData\ }"
    }
}

Then, we can use `IO` functions to transform the result across the layers of our application, and active the side effect with `.unsafePerformIO` when necessary.


fetchSomething()
    .map(jsonToOurModel)
    .unsafePerformIO()

What we get: R**eferential transparency**

If a function is total, deterministic and pure, it has a property called **referential transparency**. Referential transparency generally means that we can always replace a function with its return value without an effect on the application’s behavior.

  Applications of machine learning in cyber security you need to know about

What we get: R**eferential transparency**


func multiply(x: Int, y: Int) -> Int {
	return x * y
}
let multi = multiply(x: 2, y: 2) * multiply(x: 2, y: 2)
//Produce the same result
let multiplyResult = multi * multi

Memoization

Another consequence of having a function that is referentially transparent is that it can be memoized. Memoization is a technique used to cache already computed values, specially if they have a high cost to be computed. When a function is memoized, you can think of it as a lookup table where inputs and their corresponding outputs are saved. Subsequent invocations just retrieve the value from the table instead of actually computing it.

Example


func longRunningFunction(_ id: Int) -> Data {
	//Assuming a long running function
	return someData
}
let memoizedLongRunningFunc = memoize(longRunningFunction)
//Will compute for the first time that function and save the output
let longRunning1 = memoizedLongRunningFunc(1)
//Will use the memoized result given 0 computation cost
let longRunning2 = memoizedLongRunningFunc(1)

Function composition

At this point if we had a pure, deterministic and total functions is easy to compose them. As the functions are referentially transparent too we need some operation to combine them. The essential operation for functions is function composition. In bow we had the `compose` or `<<<` operators to receive two functions and provide a new function which behaves like applying both functions sequentially. In some cases, compose can be difficult to read right to left, or simply is not convenient to use. For those cases, Bow has utility functions that reverse the order of the arguments using `andThen` or `>>>.`

Example

We had some functions that take a String and return a String based on that String with some modifications.


func shout(_ argument: String) -> String {
	return argument.uppercased()
}
func exclaim(_ argument: String) -> String {
	return argument + "!!!"
}
func applefy(_ argument: String) -> String {
	return argument + "?"
}
func bananafy(_ argument: String) -> String {
	return argument + "?"
}
func kiwify(_ argument: String) -> String {
	return argument + "?"
}

With bow’s function composition we can do it like that:


let shoutFruitComposed = shout
                      >>> exclaim
                      >>> applefy
                      >>> bananafy
                      >>> kiwify
print(shoutFruit("I love fruit"))
// I LOVE FRUIT!!!???

Compare that latter operation with this version without composing:


func shoutFruit(_ argument: String) -> String {
					//  Hard to read
	return kiwify(bananafy(applefy(exclaim(shout(argument)))))
}
print(shoutFruitComposed("I love fruit"))
// I LOVE FRUIT!!!???

Maybe you can thing that example is not useful at all in a daily basis application. So here is a more practical example using function composition from *pointfree.co’s* site!

  Why use the NestJS framework?

Conclusions

Bow is still in early stage of development. It has been thoroughly tested but needs to be applied into some projects to evaluate its usability.

Some of the benefits of using Bow and Functional Programming are:

– Code easier to reason
– Maintainability
– Testability
– Reusability

This article only covers a small portion of all the power that Bow framework has.

Author

  • Bruno 1

    More than 10 years on software development field, and working on mobile development since 2013. Experience creating apps from scratch and working on big applications with several dependencies. Used to work with the latest technologies and take care of architectural decisions. Able to lead small iOs teams, always from the tech side.

    View all posts

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Subscribe To Our Newsletter

Get updates from our latest tech findings

Have a challenging project?

We Can Work On It Together

apiumhub software development projects barcelona
Secured By miniOrange