Function Composition in F# with Unfriendly Functions
Introduction
This is a short post looking at how to solve the problem of using F# unfriendly libraries in an F# function composition pipeline. One of my colleagues asked a question about this and I thought it might be interesting to look at some of the options available to us to solve it.
Setting Up
You can use any IDE but I will be using VSCode plus the wonderful ionide plugin.
Open VSCode in a new folder.
Open a new VSCode Terminal and create a new console app using:
In the VSCode Explorer mode, add a new folder called resources. Add a new file to the resources folder called employees.json.
Copy the following into the new file:
Replace the code in program.fs with the following:
Run the code by typing the following into the terminal and pressing Enter:
You should see some json in the terminal window.
Introducing the Problem
Whilst this works, what happens if I want to add some JsonSerializerOptions like this?
You will need to download a nuget package:
The Deserialize method has a version that takes a tuple of string and JsonSerializerOptions.
This doesn't even compile. Even if the parameters were swapped, it still wouldn't work because we are dealing with a tuple rather than curried arguments. Luckily, this isn't a difficult thing to solve but there are a few ways that we could resolve it. We are going to look at three viable solutions.
Version 1 - Custom Wrapper Function
The general approach to solving these types of problems is a layer of indirection; In this case a wrapper function.
We have wrapped our unfriendly method in a usable curried wrapper. Let's modify the getEmployees function to use this new function:
If you run it, you will see that it works as before.
We can take this even further by adding a partially applied version of the new wrapper function with the options already set, so that we only need to apply the last argument to make it run.
Again, we update the getEmployees function to use the new partially applied wrapper function:
If we run this, we will still see the expected results.
Version 2 - Generic Higher Order Function
Another option is to create a generic, in both senses of the word, function that can handle all situations that require this specific functionality:
This is the ultimate goal of pure functional programming: to find generic solutions to problems.
Now we can fit our new function into the pipeline where it will be ready to accept the last partially applied parameter through the pipeline:
Having said that this is a good thing to do, it is nowhere nearly as readable at a glance as the previous solution. Purity does not always imply superiority.
Version 3 - Inline Function
If this is a one-off requirement, rather than creating additional partially applied functions, we could use an inline anonymous function instead:
This is a really simple and elegent solution and would probably be the first approach we should try. If we needed to use the same logic elsewhere, we would start to consider using a custom wrapper function instead.
Summary
In this post we have had a look at ways to solve the problem of fitting unfriendly functions into an F# composition pipeline. We haven't used anything that wasn't covered in my 12 part Introduction to Functional Programming in F# series.
If you have any comments on this post or suggestions for new ones, send me a tweet (@ijrussell) and let me know.