Injecting functions

Let’s say we have two ways of getting the same information. We’ll use one or the other depending of the context of our code; and there is no guarantee that a third way won’t appear.

So, this is our scenario, it could’ve been written in Java:

//Returns "12" when the parameter v is "a",
//"3" if v is "b"
//and "-1" otherwise.
def fetch_info(v: String) = v match {
 case "a" => "12"
 case "b" => "3"
 case _ => "-1"
}
//concatenates the string parameters
def action1(param1: String, param2: String, common: String) =
 param1 + param2 + common
//with the first parameter, retrieves a string from fetch_info
//and concatenates it with the common parameter
def action2(param1: String, common: String) =
 fetch_info(param1) + common

val param3 = fetch_info("b")
//both return "123"
action1("1", "2", param3) == action2("a", param3)

As I said, we have two ways of getting the same information. In one case, the processing is straightforward, just concatenates the Strings. The second case could happen when this Strings are not at hand, so we need to call fetch_info to gather the missing information.

We could realize that the common operation is the concatenation of the common parameter with either the result of fetch_info or the concatenation of  param1 and param2. So we can create two functions, both use common_action to create the final result.


//common operation
def common_action(param1: String, common: String) =
 param1 + common
//concatenates the the string parameters
def action1_common(param1: String, param2: String, common: String) =
 common_action(param1 + param2, common)
//with the first parameter, retrieves a string from fetch_info
//and concatenates it with the common parameter
def action2_common(param1: String, common: String) =
 common_action(fetch_info(param1), common)
val param3 = fetch_info("b")
//both return "123"
action1_common("1", "2", param3) == action2_common("a", param3)

We could leave it there; but what if we know that a third kind of action could come up? We could implement the Bridge Pattern and/or the Adapter Pattern — hence the title of the post, the Bridge Pattern uses Dependency Injection;  but with Scala there is a another option, a functional one.


//takes two parameters and returns a function that takes a String
//and returns a String
def action1_2(param1: String, param2: String): (String) => String =
 (c) => param1 + param2 + c
//takes one parameter and returns a function that takes a String
//and returns a String
def action2_2(param1: String): (String) => String =
 (c) => fetch_info(param1) + c

val param3 = fetch_info("b")
//both return "123"
action1_2("1", "2")(param3) == action2_2("a")(param3)

I know that looks a little useless, but is good to understand what’s happening.

Both action1_2 and action2_2 return a function that takes a String and returns a String. That means that action1_2(“1”, “2”) and action2_2(“a”)  actually return functions of the form:

(String) => String

That’s why we use these closures, they create the functions we need.

(c) => param1 + param2 + c
(c) => fetch_info(param1) + c

We kind of “normalized” the operation. Now we can pass the resulting function around, gaining abstraction. That means we can do something like this:

//this operation takes a param for retrieving the "3" and
// then applies the "action" to get the final result
def big_operation(param: String, action: (String) => String): String =
 action(fetch_info(param))
//both return "123"
big_operation("b", action1_2("1", "2")) == big_operation("b", action2_2("a"))

big_operation is isolated from the function that obtains the information big_operation needs. Which means that if a third way of getting “123” appears, big_operation wouldn’t have to change(we are assuming that big_operation is big, with a lot of logic).

But how to reason about this? Is no like we want to keep refactoring code for the rest of our life; well, we will, but the refactorization could be reduced if we came up something like big_operation since the beginning.

In this case, the best way of finding a pattern is to find the common operation:

def common_action(param1: String, common: String) =
 param1 + common

Not the actual function of course, but the pattern of having one common element that has to be used with some other calculable element. Meaning:


(common) => param1 + common

Assuming we have param1 in scope. Which we have in action1_2(“1”, “2”) and action2_2(“a”). That usually means that we can “bind” the common parameter as dependency to partial operations. That sounded weird, but it implies that we need to return a function that takes common as a parameter. I said “partial”,  because we are actually returning a function that has already a few parameters applied, but still need another(s) to return a proper result.

That’s why we returned the closures, and that’s a cool way of doing Dependency Injection (-ish) with Functional Programming

Is not a bad idea to take a look to:  The Neophyte’s Guide to Scala Part 11: Currying and Partially Applied Functions. And the whole series for that matter.