Function Composition & Swift 2.0
Excited by the announcement of Swift 2.0 at the WWDC 2015 I decided to revise some of my function composition code and adapt it to the new error handling pattern in Swift 2.0. I am using function composing quite often and I think it is a really great pattern— let me show you why.
The Example: Handle Server Response
Lets get a practical example, one from the real world instead of a hypothetical one, one iOS developers have to cope with quite often:
Lets say a network API returns a plain NSData object and there is a need to unmarshal this response into a fancy domain model of some sorts.
The steps needed for this could be formulated as following:
- Validate JSON / Transform the NSData object into a more handleable JSON structure to ease parsing. (The most basic representation is would be a dictionary)
- Unwrap success / failure JSON — e.g. the server might have returned application errors in the response instead of your domain model
- Unmarshal single JSON representation into domain model
Not really hard to gasp but the challenge here is to create an easy to maintain code that is readable, flexible and reusable for lets say all other endpoints of the API (more on this later).
An attempt to implement this is to create a function for each step:
I am not going to much into implementation details as these are examples — just giving a short description what each function will do:
parseJSONFromNSData — Will take an arbitrary NSData object and parse it into a custom JSON value (e.g. dictionary / array representation of the JSON structure).
validateJSONResponse — Will check the JSON value for an embedded error returned by the server. If you are a experienced Swift developer you might would like to use some sort of Either type instead of that two dimensional tuple. I simplified here…
unmarshalJSON — Receives a JSON value and the Type that conforms to JSONUnmarshallable and represents the domain model. The protocol just defines that the object is instantiatable from a JSON value.
I deliberately chose functions instead of methods. Thus we end up with distinct fragments of code that do not access any shared state which might be encapsulated in an object - instead every data accessible by a function should passed on invocation as arguments. Unfortunately in Swift nothing stops you from accessing variables from the outer scope — so its up to ones discipline to follow that rule.
What About Failures
Now that the functions are defined — professional developers should be really interested in the possible failures of their code. I mean alone parseJSONFromNSData could fail so many reasons. A common anti pattern is to make all return types Optionals which fails terribly if one is interested in the reason / context of the failure. In validateJSONResponse the optional NSError could be used pretty much like the error handling pattern in the Objective C standard library.
In Swift 2.0 a new error handling model was introduced much similar to the one of C# or Java. Unlike in Objective C developers are now encouraged to use exception handling! I am not going much into the pros and cons of this into depth but I heartily recommend you this article.
So with exception handling the functions could look like this:
Now each function has the throws keyword attached after its arguments that indicates that this function might throw an error type which you need to catch or propagate. This might be an advantage over Java / C# as the compiler will help* if one forgets to handle the error.
*just refuses compiling
Note that I defined custom enums conforming to the ErrorType protocol to represent all possible errors so they failures are now distinguishable and have all related data associated to them. Basic enumerations leaves the display of the error to the user facing layer — thats actually a good lean practise!
Now errors can be thrown & handled like this:
Errors can be handled by embedding the throwing functions in a do catch block by preceding each function call with the keyword try. If a function throws an error the do block is exited immediately. Thus further code execution in the block is prevented in favour to handle the error in one of the matching catch blocks. Errors can be distinguished by their error types (as defined in the enum) or handled as an generic error as the last catch block shows.
Note that handleResponse is somewhat silly as its only attempt to handle errors is printing them out to the console — again this is just for the sake of demonstration.
To find out more about error handling in Swift you can read the official language documentation from Apple.
Why Composing Functions
In the last code snippet we kind of composed our three original functions into one big named handleResponse. This is fine but requires the creation of an additional function with some boilerplate. There is still some code clutter to save the end result of each function and pass into into the next one. More readable, reusable and especially flexible code could be produced by composing functions without the need to create a container function yourself.
The pattern of function composing is well known in languages like Haskell or F#. It basically means to make one function out of a chain of at least two functions where the argument type of the first is the return type of the second function and so on. The result of this composition is a function itself that has the argument type of the first and a return type of the last function.
To exemplify the composition have look at the following combine function:
The return type is a function* which executes the first function takes its output and executes the second function with it and returns the result.
*Combine returns always a function that throws which will force the compiler to refuse compiling if you don’t handle the error — even if you combine two functions that does not throw. You can overcome this by defining a second combine function to combine two non throwing functions. I leave this up to you…
In order to compose functions they need to meet two criteria:
- Each function must receive only one input argument
- Each function argument type must match the return type of its predecessor function (in the context of the chain of function that is to compose)
The previously defined functions meet rule 2. already but to satisfy 1. the functions with more then one input parameter need to be normalised. The only function that needs normalisation is unmarshalJSON as its original implementation receives JSON and Type. This can be solved by currying:
Invoking unmarshalJSON with only the Type parameter generates a single argument function that can be used with combine. This is useful as the type parameter does not really belong into the composition chain as does not depend of the result of any preceding function.
The composition result is pretty much the same handleRespone function as in the previous code listing — but now it throws any of the ErrorTypes of the functions it is composed of.
Combining functions is great to read and helps factoring your code but creates always a small overhead and step by step debugging might get unpleasant as one will see unrelated code of the combine function itself. By setting proper breakpoints this might be less of an issue.
Compose Operator >>>
Using the combine function does not really meet the criteria of easy to read code. It frankly causes me some nightmares about parentheses…
I am not really a fan of using custom operators for everything but that is one of the few cases where it makes sense*. To avoid confusion I choose to create a custom binary operator >>> which does exactly the same as combine — it is just syntactic sugar. And here is the magic:
Now the >>> operator can be used to combine those “normalised” functions at pleasure. The resulting code is pretty much self describing (if you named your functions wisely) and creating new combined functions is a piece of cake.
Wrap It Up
Functions compositions are easy to extend and thus far more flexible and usually lead to more factorised, reusable and readable code.
Swift 2.0 offers a new model for error handling which is strict so you won’t forget to handle or propagate errors that plays really nice with function composing.
Creating a custom compose operator helps to make the code more readable as we all love syntactic sugar.
As you might noticed this concept is only applicable to functions that execute synchronously for now. I did not forget async functions and would like to write a second article about them very soon!
Here is also a great article about railway oriented programming you should not miss out. It is basically a functional concept for error handling. Or a practical application of monads if you like.
I hope you liked the article and got some inspiration for your own project. Every feedback is highly appreciated.