Blog by Railsware

Composing functions in Swift

In Swift – the new programming language introduced by Apple – functions are first class citizens. This basically means, that a function can be passed as a parameter, returned from another function or assigned to a value. It allows us to do a lot of useful stuff with them.

Function decorators

Let’s use the concepts of function as a first class citizen to implement some basic function decorator.

Assign functions to variables

func greeting(firstName: String, lastName: String) -> String {
    return "Hello, " + firstName + " " + lastName + "!"
}
let greetSomeone = greeting
println(greetSomeone("John", "Doe"))    // => Hello, John Doe!

Functions can be passed as parameters to other functions

func johnDoeFunction(function: (String, String) -> String) -> String {
    return function("John", "Doe")
}
println(johnDoeFunction(greeting))      // => Hello, John Doe!

Functions can return other functions

func composeHelloFunction() -> (String -> String) {    
    func hello(name: String) -> String {
        return "Hello, " + name
    }
    return hello
}
let helloFunc = composeHelloFunction()
println(helloFunc("John"))              // => Hello, John

This concept allows us to implement function decorators in Swift.

Function decorators work as wrappers to existing functions, modifying the behavior of the code before and after a target function, without the need to modify the function itself. They augment the original functionality, thus decorate it.

Using the concepts above let’s write a simple decorator that wraps the String output of another function by some html tag.

func hello(name: String) -> String {
    return "Hello, " + name
}

func bold(function: String -> String) -> (String -> String) {
    func decoratedBold(text: String) -> String {
        return "" + function(text) + ""
    }
    return decoratedBold
}
let boldHello = bold(hello)
println(boldHello("Vladimir"))          // => Hello, Vladimir

We wrote a function that takes another function as an argument, generates a new function, augmenting the work of the original function, and returns the generated function so we can use it anywhere. Decorating function also allows us to insert some behaviour before/after function call or combine multiple functions into one.

In the example above we had to explicitly specify the signature of the function to decorate: func bold(function: String -> String). We’ve implemented bold decorator so that it decorates a function from one String value, which returns another String value. But what if one would like to create bold decorator which accepts functions with different signatures?

Abstract Functor

Let’s define an abstract Functor class. By ‘Functor’ let’s assume a class, which wraps any function and allows to call that function.

class F<T1, T2> {
    
    let f: T1 -> T2
    
    init(function: T1 -> T2) {
        self.f = function
    }
    
    func run(args: T1) -> T2 {
        return f(args)
    }
}

This class allows us to wrap any function and call it somewhere:

func logger(text: String) {
    println("LOG: \(text)")
}
let loggerFunc = F(logger)
loggerFunc.run("Hello")    // => LOG: Hello

Using the Functor class we can now rewrite bold decorator to accept functions with different number of parameters:

func bold(function: F<T1, String>) -> F<T1, String> {
    func decoratedBold(args: T1) -> String {
        return "" + function.run(args) + ""
    }
    return F(decoratedBold)
}

let boldGreeting = bold(F(greeting))
println(boldGreeting.run("John", "Doe"))    // => Hello, John Doe!

let boldHello = bold(F(hello))
println(boldHello.run("Vladimir"))          // => Hello, Vladimir

Composing functions

Another interesting approach of using function decorators is composing several functions into single one.

Swift allows overloading basic operators. Let’s try to overload + operator to accept two functions and compose them into one function.

func +<T1, T2>(beforeHook: F<(), ()>, function: F<T1, T2>) -> (T1 -> T2) {

    func composedFunc(args: T1) -> T2 {
        beforeHook.run()
        return function.run(args)
    }

    return composedFunc
}

func +<T1, T2>(function: F<T1, T2>, afterHook: F<T2, ()>) -> (T1 -> T2) {  
 
    func composedFunc(args: T1) -> T2 {
        var result = function.run(args)
        afterHook.run(result)
        return result
    } 
  
    return composedFunc
}

Above you can see two overloads of + operator which accept functors of different types and combine them into a single function.
The first overloaded version runs a Void -> Void function before main function, and the second one runs main function,
then passes its result to another T2 -> Void function and returns the result of the main function.

Let’s write an example of using this approach:

func logger(text: String) {
    println("LOG: \(text)")
}

func request(url: String) -> String {
    return "Success 200"
}

let logRequest = F(request) + F(logger)
logRequest("http://some.awesome.url")       // => "LOG: Success 200"

let composedRequest = F({ println("Request is fired!") }) + F(request)
println(composedRequest("http://some.awesome.url"))
// => Request is fired!
// => Success 200 

We could also implement + operator overload for plain functions, not for Functor type, but overloading operators for basic types may be a bad practice.

Another approach of composing functions may be implemented with some helper classes and without overloading operators. As an example of doing this
take a look at Before and After classes here. They allow us to compose functions in another way:

let composedRequest = Before(request).run({ println("Request is fired!") })
let loggedRequest = After(request).run(logger)

Retry/repeat function call

One another way of using function decorators and Functor class, implemented above, is to write Repeat and Retry decorators. It will allow to run a single function multiple times or until some condition is met.

class F<T1, T2> {
    
    ...
    
    func repeat(args: T1, times: Int) -> T2 {
        for i in 1..times {
            f(args)
        }
        return f(args)
    }

    func retry(args: T1, maxTries: Int, condition: () -> Bool) -> T2 {
        var tries = 0
        var result: T2?
        
        while !condition() && tries < maxTries {
            result = f(args)
            tries++
        }    
    
        return result!
    }
}

Code above allows us to implement multiple calling of a function mechanism like this:

func greeting(firstName: String, lastName: String) {
    print("Hello, " + firstName + " " + lastName + "!")
}

F(greeting).repeat(("John", "Doe"), times: 3)    // => Hello, John Doe! Hello, John Doe! Hello, John Doe!

Or we can run a function until some condition is met or until we have performed some number of tries:

var requestResult: Int = 0
func randomRequest(url: String) {
    let result = Int(arc4random_uniform(UInt32(2)))
    if result > 0 {
        requestResult = 200
    } else {
        requestResult = 404
    }
}

F(randomRequest).retry("http://some.awesome.url", maxTries: 5, condition: { requestResult == 200 })

Conclusion

This article demonstrates a basic approach of decorating and composing functions in Swift. It is pretty easy to implement Decorator pattern in Swift and many useful decorators may be implemented using the described approach.

Having functions as first class citizens may allow creating clean, short and extensible code, which will be pretty easy to understand.

All the code examples for this article could be found in this repository.

Exit mobile version