Functional Adaptation
- by Charles Courchaine
In real life and OO programming we’re often faced with using adapters, DVI to VGA, 1/4” to 1/8” audio connections, 110V to 220V, wrapping an incompatible interface with a new one, and so on. Where the adapter pattern is generally considered for interfaces and classes a similar technique can be applied to method signatures. To be fair, this adaptation is generally used to reduce the number of parameters but I’m sure there are other clever possibilities to be had. As Jan questioned in the last post, how can we use a common method to execute an action if the action has a differing number of parameters, going back to the greeting example it was suggested having an AddName method that takes a first and last name as parameters. This is exactly what we’ll address in this post. Let’s set the stage with some review and some code changes. First, our method that handles the setup/tear-down infrastructure for our WCF service: 1: private static TResult ExecuteGreetingFunc<TResult>(Func<IGreeting, TResult> theGreetingFunc)
2: {
3: IGreeting aGreetingService = null;
4: try
5: {
6: aGreetingService = GetGreetingChannel();
7: return theGreetingFunc(aGreetingService);
8: }
9: finally
10: {
11: CloseWCFChannel((IChannel)aGreetingService);
12: }
13: }
Our original AddName method:
1: private static string AddName(string theName)
2: {
3: return ExecuteGreetingFunc<string>(theGreetingService => theGreetingService.AddName(theName));
4: }
Our new AddName method:
1: private static int AddName(string firstName, string lastName)
2: {
3: return ExecuteGreetingFunc<int>(theGreetingService => theGreetingService.AddName(firstName, lastName));
4: }
Let’s change the AddName method, just a little bit more for this example and have it take the greeting service as a parameter.
1: private static int AddName(IGreeting greetingService, string firstName, string lastName)
2: {
3: return greetingService.AddName(firstName, lastName);
4: }
The new signature of AddName using the Func delegate is now Func<IGreeting, string, string, int>, which can’t be used with ExecuteGreetingFunc as is because it expects Func<IGreeting, TResult>. Somehow we have to eliminate the two string parameters before we can use this with our existing method. This is where we need to adapt AddName to match what ExecuteGreetingFunc expects, and we’ll do so in the following progression.
1: Func<IGreeting, string, string, int> -> Func<IGreeting, string, int>
2: Func<IGreeting, string, int> -> Func<IGreeting, int>
For the first step, we’ll create a method using the lambda syntax that will “eliminate” the last name parameter:
1: string lastNameToAdd = "Smith";
2: //Func<IGreeting, string, string, int> -> Func<IGreeting, string, int>
3: Func<IGreeting, string, int> addName = (greetingService, firstName) => AddName(greetingService, firstName, lastNameToAdd);
The new addName method gets us one step close to the signature we need. Let’s say we’re going to call this in a loop to add several names, we’ll take the final step from Func<IGreeting, string, int> -> Func<IGreeting, int> in line as a lambda passed to ExecuteGreetingFunc like so:
1: List<string> firstNames = new List<string>() { "Bob", "John" };
2: int aID;
3: foreach (string firstName in firstNames)
4: {
5: //Func<IGreeting, string, int> -> Func<IGreeting, int>
6: aID = ExecuteGreetingFunc<int>(greetingService => addName(greetingService, firstName));
7: Console.WriteLine(GetGreeting(aID));
8: }
If for some reason you needed to break out the lambda on line 6 you could replace it with
1: aID = ExecuteGreetingFunc<int>(ApplyAddName(addName, firstName));
and use this method:
1: private static Func<IGreeting, int> ApplyAddName(Func<IGreeting, string, int> addName, string lastName)
2: {
3: return greetingService => addName(greetingService, lastName);
4: }
Splitting out a lambda into its own method is useful both in this style of coding as well as LINQ queries to improve the debugging experience. It is not strictly necessary to break apart the steps & functions as was shown above; the lambda in line 6 (of the foreach example) could include both the last name and first name instead of being composed of two functions.
The process demonstrated above is one of partially applying functions, this could have also been done with Currying (also see Dustin Campbell’s excellent post on Currying for the canonical curried add example). Matthew Podwysocki also has some good posts explaining both Currying and partial application and a follow up post that further clarifies the difference between Currying and partial application. In either technique the ultimate goal is to reduce the number of parameters passed to a function. Currying makes it a single parameter passed at each step, where partial application allows one to use multiple parameters at a time as we’ve done here. This technique isn’t for everyone or every problem, but can be extremely handy when you need to adapt a call to something you don’t control.